2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // common.c -- misc functions used in client and server
31 cvar_t registered = {CVAR_CLIENT | CVAR_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {CVAR_CLIENT | CVAR_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
34 // FIXME: Find a better place for these.
35 cvar_t cl_playermodel = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playermodel", "", "current player model in Nexuiz/Xonotic"};
36 cvar_t cl_playerskin = {CVAR_CLIENT | CVAR_SERVER | CVAR_USERINFO | CVAR_SAVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"};
38 char com_token[MAX_INPUTLINE];
40 //===========================================================================
42 void SZ_Clear (sizebuf_t *buf)
47 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
51 if (buf->cursize + length > buf->maxsize)
53 if (!buf->allowoverflow)
54 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
56 if (length > buf->maxsize)
57 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
59 buf->overflowed = true;
60 Con_Print("SZ_GetSpace: overflow\n");
64 data = buf->data + buf->cursize;
65 buf->cursize += length;
70 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
72 memcpy (SZ_GetSpace(buf,length),data,length);
75 // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
76 // attention, it has been eradicated from here, its only (former) use in
79 static const char *hexchar = "0123456789ABCDEF";
80 void Com_HexDumpToConsole(const unsigned char *data, int size)
84 char *cur, *flushpointer;
85 const unsigned char *d;
87 flushpointer = text + 512;
95 *cur++ = hexchar[(i >> 12) & 15];
96 *cur++ = hexchar[(i >> 8) & 15];
97 *cur++ = hexchar[(i >> 4) & 15];
98 *cur++ = hexchar[(i >> 0) & 15];
101 for (j = 0;j < 16;j++)
105 *cur++ = hexchar[(d[j] >> 4) & 15];
106 *cur++ = hexchar[(d[j] >> 0) & 15];
117 for (j = 0;j < 16;j++)
121 // color change prefix character has to be treated specially
122 if (d[j] == STRING_COLOR_TAG)
124 *cur++ = STRING_COLOR_TAG;
125 *cur++ = STRING_COLOR_TAG;
127 else if (d[j] >= (unsigned char) ' ')
137 if (cur >= flushpointer || i >= size)
146 void SZ_HexDumpToConsole(const sizebuf_t *buf)
148 Com_HexDumpToConsole(buf->data, buf->cursize);
152 //============================================================================
158 Word wraps a string. The wordWidth function is guaranteed to be called exactly
159 once for each word in the string, so it may be stateful, no idea what that
160 would be good for any more. At the beginning of the string, it will be called
161 for the char 0 to initialize a clean state, and then once with the string " "
162 (a space) so the routine knows how long a space is.
164 In case no single character fits into the given width, the wordWidth function
165 must return the width of exactly one character.
167 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
169 The sum of the return values of the processLine function will be returned.
172 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
174 // Logic is as follows:
176 // For each word or whitespace:
177 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
178 // Space found? Always add it to the current line, no matter if it fits.
179 // Word found? Check if current line + current word fits.
180 // If it fits, append it. Continue.
181 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
183 qboolean isContinuation = false;
185 const char *startOfLine = string;
186 const char *cursor = string;
187 const char *end = string + length;
188 float spaceUsedInLine = 0;
189 float spaceUsedForWord;
195 wordWidth(passthroughCW, NULL, &dummy, -1);
197 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
201 char ch = (cursor < end) ? *cursor : 0;
204 case 0: // end of string
205 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
207 case '\n': // end of line
208 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
209 isContinuation = false;
211 startOfLine = cursor;
215 spaceUsedInLine += spaceWidth;
219 while(cursor + wordLen < end)
221 switch(cursor[wordLen])
233 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
234 if(wordLen < 1) // cannot happen according to current spec of wordWidth
237 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
239 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
241 // we can simply append it
243 spaceUsedInLine += spaceUsedForWord;
247 // output current line
248 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
249 isContinuation = true;
250 startOfLine = cursor;
252 spaceUsedInLine = continuationWidth + spaceUsedForWord;
261 qboolean isContinuation = false;
262 float currentWordSpace = 0;
263 const char *currentWord = 0;
264 float minReserve = 0;
266 float spaceUsedInLine = 0;
267 const char *currentLine = 0;
268 const char *currentLineEnd = 0;
269 float currentLineFinalWhitespace = 0;
273 minReserve = charWidth(passthroughCW, 0);
274 minReserve += charWidth(passthroughCW, ' ');
276 if(maxWidth < continuationWidth + minReserve)
277 maxWidth = continuationWidth + minReserve;
279 charWidth(passthroughCW, 0);
281 for(p = string; p < string + length; ++p)
284 float w = charWidth(passthroughCW, c);
289 currentWordSpace = 0;
295 spaceUsedInLine = isContinuation ? continuationWidth : 0;
301 // 1. I can add the word AND a space - then just append it.
302 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
304 currentLineEnd = p; // note: space not included here
305 currentLineFinalWhitespace = w;
306 spaceUsedInLine += currentWordSpace + w;
308 // 2. I can just add the word - then append it, output current line and go to next one.
309 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
311 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
313 isContinuation = true;
315 // 3. Otherwise, output current line and go to next one, where I can add the word.
316 else if(continuationWidth + currentWordSpace + w <= maxWidth)
319 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
320 currentLine = currentWord;
321 spaceUsedInLine = continuationWidth + currentWordSpace + w;
323 currentLineFinalWhitespace = w;
324 isContinuation = true;
326 // 4. We can't even do that? Then output both current and next word as new lines.
331 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
332 isContinuation = true;
334 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
336 isContinuation = true;
342 // 1. I can add the word - then do it.
343 if(spaceUsedInLine + currentWordSpace <= maxWidth)
345 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
347 // 2. Otherwise, output current line, next one and make tabula rasa.
352 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
353 isContinuation = true;
355 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
359 isContinuation = false;
363 currentWordSpace += w;
365 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
367 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
370 // this word cannot join ANY line...
371 // so output the current line...
374 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
375 isContinuation = true;
378 // then this word's beginning...
381 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
382 float pieceWidth = maxWidth - continuationWidth;
383 const char *pos = currentWord;
384 currentWordSpace = 0;
386 // reset the char width function to a state where no kerning occurs (start of word)
387 charWidth(passthroughCW, ' ');
390 float w = charWidth(passthroughCW, *pos);
391 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
393 // print everything until it
394 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
397 currentWordSpace = 0;
399 currentWordSpace += w;
402 // now we have a currentWord that fits... set up its next line
403 // currentWordSpace has been set
404 // currentWord has been set
405 spaceUsedInLine = continuationWidth;
406 currentLine = currentWord;
408 isContinuation = true;
412 // we have a guarantee that it will fix (see if clause)
413 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
415 // and use the rest of this word as new start of a line
416 currentWordSpace = w;
418 spaceUsedInLine = continuationWidth;
421 isContinuation = true;
430 currentWordSpace = 0;
433 if(currentLine) // Same procedure as \n
435 // Can I append the current word?
436 if(spaceUsedInLine + currentWordSpace <= maxWidth)
437 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
442 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
443 isContinuation = true;
445 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
455 COM_ParseToken_Simple
457 Parse a token out of a string
460 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
464 const char *data = *datapointer;
481 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
491 // handle Windows line ending
492 if (data[0] == '\r' && data[1] == '\n')
495 if (parsecomments && data[0] == '/' && data[1] == '/')
498 while (*data && *data != '\n' && *data != '\r')
502 else if (parsecomments && data[0] == '/' && data[1] == '*')
506 while (*data && (data[0] != '*' || data[1] != '/'))
514 else if (*data == '\"')
517 for (data++;*data && *data != '\"';data++)
520 if (*data == '\\' && parsebackslash)
529 if (len < (int)sizeof(com_token) - 1)
530 com_token[len++] = c;
538 else if (*data == '\r')
540 // translate Mac line ending to UNIX
541 com_token[len++] = '\n';data++;
546 else if (*data == '\n')
549 com_token[len++] = *data++;
557 for (;!ISWHITESPACE(*data);data++)
558 if (len < (int)sizeof(com_token) - 1)
559 com_token[len++] = *data;
568 COM_ParseToken_QuakeC
570 Parse a token out of a string
573 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
577 const char *data = *datapointer;
594 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
604 // handle Windows line ending
605 if (data[0] == '\r' && data[1] == '\n')
608 if (data[0] == '/' && data[1] == '/')
611 while (*data && *data != '\n' && *data != '\r')
615 else if (data[0] == '/' && data[1] == '*')
619 while (*data && (data[0] != '*' || data[1] != '/'))
627 else if (*data == '\"' || *data == '\'')
631 for (data++;*data && *data != quote;data++)
643 if (len < (int)sizeof(com_token) - 1)
644 com_token[len++] = c;
652 else if (*data == '\r')
654 // translate Mac line ending to UNIX
655 com_token[len++] = '\n';data++;
660 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
663 com_token[len++] = *data++;
671 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
672 if (len < (int)sizeof(com_token) - 1)
673 com_token[len++] = *data;
682 COM_ParseToken_VM_Tokenize
684 Parse a token out of a string
687 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
691 const char *data = *datapointer;
708 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
718 // handle Windows line ending
719 if (data[0] == '\r' && data[1] == '\n')
722 if (data[0] == '/' && data[1] == '/')
725 while (*data && *data != '\n' && *data != '\r')
729 else if (data[0] == '/' && data[1] == '*')
733 while (*data && (data[0] != '*' || data[1] != '/'))
741 else if (*data == '\"' || *data == '\'')
745 for (data++;*data && *data != quote;data++)
757 if (len < (int)sizeof(com_token) - 1)
758 com_token[len++] = c;
766 else if (*data == '\r')
768 // translate Mac line ending to UNIX
769 com_token[len++] = '\n';data++;
774 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
777 com_token[len++] = *data++;
785 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
786 if (len < (int)sizeof(com_token) - 1)
787 com_token[len++] = *data;
796 COM_ParseToken_Console
798 Parse a token out of a string, behaving like the qwcl console
801 int COM_ParseToken_Console(const char **datapointer)
804 const char *data = *datapointer;
817 for (;ISWHITESPACE(*data);data++)
827 if (*data == '/' && data[1] == '/')
830 while (*data && *data != '\n' && *data != '\r')
834 else if (*data == '\"')
837 for (data++;*data && *data != '\"';data++)
839 // allow escaped " and \ case
840 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
842 if (len < (int)sizeof(com_token) - 1)
843 com_token[len++] = *data;
853 for (;!ISWHITESPACE(*data);data++)
854 if (len < (int)sizeof(com_token) - 1)
855 com_token[len++] = *data;
868 Returns the position (1 to argc-1) in the program's argument list
869 where the given parameter apears, or 0 if not present
872 int COM_CheckParm (const char *parm)
876 for (i=1 ; i<sys.argc ; i++)
879 continue; // NEXTSTEP sometimes clears appkit vars.
880 if (!strcmp (parm,sys.argv[i]))
891 Used by view and sv_user
894 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
900 AngleVectors (angles, NULL, right, NULL);
901 side = DotProduct (velocity, right);
902 sign = side < 0 ? -1 : 1;
905 if (side < velocityval)
906 side = side * angleval / velocityval;
914 //===========================================================================
921 void COM_Init_Commands (void)
924 char com_cmdline[MAX_INPUTLINE];
926 Cvar_RegisterVariable (®istered);
927 Cvar_RegisterVariable (&cmdline);
928 Cvar_RegisterVariable(&cl_playermodel);
929 Cvar_RegisterAlias(&cl_playermodel, "_cl_playermodel");
930 Cvar_RegisterVariable(&cl_playerskin);
931 Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin");
933 // reconstitute the command line for the cmdline externally visible cvar
935 for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
938 if (strstr(sys.argv[j], " "))
940 // arg contains whitespace, store quotes around it
941 // This condition checks whether we can allow to put
942 // in two quote characters.
943 if (n >= ((int)sizeof(com_cmdline) - 2))
945 com_cmdline[n++] = '\"';
946 // This condition checks whether we can allow one
947 // more character and a quote character.
948 while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
949 // FIXME: Doesn't quote special characters.
950 com_cmdline[n++] = sys.argv[j][i++];
951 com_cmdline[n++] = '\"';
955 // This condition checks whether we can allow one
957 while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
958 com_cmdline[n++] = sys.argv[j][i++];
960 if (n < ((int)sizeof(com_cmdline) - 1))
961 com_cmdline[n++] = ' ';
966 Cvar_SetQuick(&cmdline, com_cmdline);
973 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
976 char *va(char *buf, size_t buflen, const char *format, ...)
980 va_start (argptr, format);
981 dpvsnprintf (buf, buflen, format,argptr);
988 //======================================
990 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
996 # define snprintf _snprintf
997 # define vsnprintf _vsnprintf
1001 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1006 va_start (args, format);
1007 result = dpvsnprintf (buffer, buffersize, format, args);
1014 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1018 #if _MSC_VER >= 1400
1019 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1021 result = vsnprintf (buffer, buffersize, format, args);
1023 if (result < 0 || (size_t)result >= buffersize)
1025 buffer[buffersize - 1] = '\0';
1033 //======================================
1035 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1040 if(utf8_enable.integer)
1043 while(*in && size_out > 1)
1046 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1047 ch = u8_tolower(ch);
1048 n = u8_fromchar(ch, out, size_out);
1057 while (*in && size_out > 1)
1059 if (*in >= 'A' && *in <= 'Z')
1060 *out++ = *in++ + 'a' - 'A';
1068 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1073 if(utf8_enable.integer)
1076 while(*in && size_out > 1)
1079 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1080 ch = u8_toupper(ch);
1081 n = u8_fromchar(ch, out, size_out);
1090 while (*in && size_out > 1)
1092 if (*in >= 'a' && *in <= 'z')
1093 *out++ = *in++ + 'A' - 'a';
1101 int COM_StringBeginsWith(const char *s, const char *match)
1103 for (;*s && *match;s++, match++)
1109 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1111 int argc, commentprefixlength;
1115 tokenbufend = tokenbuf + tokenbufsize;
1117 commentprefixlength = 0;
1119 commentprefixlength = (int)strlen(commentprefix);
1120 while (*l && *l != '\n' && *l != '\r')
1122 if (!ISWHITESPACE(*l))
1124 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1126 while (*l && *l != '\n' && *l != '\r')
1130 if (argc >= maxargc)
1132 argv[argc++] = tokenbuf;
1136 while (*l && *l != '"')
1138 if (tokenbuf >= tokenbufend)
1147 while (!ISWHITESPACE(*l))
1149 if (tokenbuf >= tokenbufend)
1154 if (tokenbuf >= tokenbufend)
1175 COM_StringLengthNoColors
1177 calculates the visible width of a color coded string.
1179 *valid is filled with TRUE if the string is a valid colored string (that is, if
1180 it does not end with an unfinished color code). If it gets filled with FALSE, a
1181 fix would be adding a STRING_COLOR_TAG at the end of the string.
1183 valid can be set to NULL if the caller doesn't care.
1185 For size_s, specify the maximum number of characters from s to use, or 0 to use
1186 all characters until the zero terminator.
1190 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1192 const char *end = size_s ? (s + size_s) : NULL;
1196 switch((s == end) ? 0 : *s)
1202 case STRING_COLOR_TAG:
1204 switch((s == end) ? 0 : *s)
1206 case STRING_COLOR_RGB_TAG_CHAR:
1207 if (s+1 != end && isxdigit(s[1]) &&
1208 s+2 != end && isxdigit(s[2]) &&
1209 s+3 != end && isxdigit(s[3]) )
1214 ++len; // STRING_COLOR_TAG
1215 ++len; // STRING_COLOR_RGB_TAG_CHAR
1217 case 0: // ends with unfinished color code!
1222 case STRING_COLOR_TAG: // escaped ^
1225 case '0': case '1': case '2': case '3': case '4':
1226 case '5': case '6': case '7': case '8': case '9': // color code
1228 default: // not a color code
1229 ++len; // STRING_COLOR_TAG
1230 ++len; // the character
1245 COM_StringDecolorize
1247 removes color codes from a string.
1249 If escape_carets is true, the resulting string will be safe for printing. If
1250 escape_carets is false, the function will just strip color codes (for logging
1253 If the output buffer size did not suffice for converting, the function returns
1254 FALSE. Generally, if escape_carets is false, the output buffer needs
1255 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1256 bytes. In any case, the function makes sure that the resulting string is
1259 For size_in, specify the maximum number of characters from in to use, or 0 to use
1260 all characters until the zero terminator.
1264 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1266 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1267 const char *end = size_in ? (in + size_in) : NULL;
1272 switch((in == end) ? 0 : *in)
1277 case STRING_COLOR_TAG:
1279 switch((in == end) ? 0 : *in)
1281 case STRING_COLOR_RGB_TAG_CHAR:
1282 if (in+1 != end && isxdigit(in[1]) &&
1283 in+2 != end && isxdigit(in[2]) &&
1284 in+3 != end && isxdigit(in[3]) )
1289 APPEND(STRING_COLOR_TAG);
1291 APPEND(STRING_COLOR_TAG);
1292 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1294 case 0: // ends with unfinished color code!
1295 APPEND(STRING_COLOR_TAG);
1296 // finish the code by appending another caret when escaping
1298 APPEND(STRING_COLOR_TAG);
1301 case STRING_COLOR_TAG: // escaped ^
1302 APPEND(STRING_COLOR_TAG);
1303 // append a ^ twice when escaping
1305 APPEND(STRING_COLOR_TAG);
1307 case '0': case '1': case '2': case '3': case '4':
1308 case '5': case '6': case '7': case '8': case '9': // color code
1310 default: // not a color code
1311 APPEND(STRING_COLOR_TAG);
1326 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1332 keylength = strlen(key);
1333 if (valuelength < 1 || !value)
1335 Con_Printf("InfoString_GetValue: no room in value\n");
1339 if (strchr(key, '\\'))
1341 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1344 if (strchr(key, '\"'))
1346 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1351 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1354 while (buffer[pos] == '\\')
1356 if (!memcmp(buffer + pos+1, key, keylength) &&
1357 (buffer[pos+1 + keylength] == 0 ||
1358 buffer[pos+1 + keylength] == '\\'))
1360 pos += 1 + (int)keylength; // Skip \key
1361 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1362 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1363 value[j] = buffer[pos+j];
1367 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1368 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1369 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1370 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1372 // if we reach this point the key was not found
1376 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1384 keylength = strlen(key);
1385 if (strchr(key, '\\') || strchr(value, '\\'))
1387 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1390 if (strchr(key, '\"') || strchr(value, '\"'))
1392 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1397 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1400 while (buffer[pos] == '\\')
1402 if (!memcmp(buffer + pos+1, key, keylength) &&
1403 (buffer[pos+1 + keylength] == 0 ||
1404 buffer[pos+1 + keylength] == '\\'))
1406 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1407 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1408 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1409 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1411 // if we found the key, find the end of it because we will be replacing it
1413 if (buffer[pos] == '\\')
1415 pos2 += 1 + (int)keylength; // Skip \key
1416 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1417 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1419 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1421 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1426 // set the key/value and append the remaining text
1427 char tempbuffer[MAX_INPUTLINE];
1428 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1429 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
1433 // just remove the key from the text
1434 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1438 void InfoString_Print(char *buffer)
1441 char key[MAX_INPUTLINE];
1442 char value[MAX_INPUTLINE];
1445 if (*buffer != '\\')
1447 Con_Printf("InfoString_Print: corrupt string\n");
1450 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1451 if (i < (int)sizeof(key)-1)
1454 if (*buffer != '\\')
1456 Con_Printf("InfoString_Print: corrupt string\n");
1459 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1460 if (i < (int)sizeof(value)-1)
1461 value[i++] = *buffer;
1463 // empty value is an error case
1464 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1468 //========================================================
1469 // strlcat and strlcpy, from OpenBSD
1472 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1474 * Permission to use, copy, modify, and distribute this software for any
1475 * purpose with or without fee is hereby granted, provided that the above
1476 * copyright notice and this permission notice appear in all copies.
1478 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1479 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1480 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1481 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1482 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1483 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1484 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1487 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
1488 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
1491 #ifndef HAVE_STRLCAT
1493 strlcat(char *dst, const char *src, size_t siz)
1495 register char *d = dst;
1496 register const char *s = src;
1497 register size_t n = siz;
1500 /* Find the end of dst and adjust bytes left but don't go past end */
1501 while (n-- != 0 && *d != '\0')
1507 return(dlen + strlen(s));
1508 while (*s != '\0') {
1517 return(dlen + (s - src)); /* count does not include NUL */
1519 #endif // #ifndef HAVE_STRLCAT
1522 #ifndef HAVE_STRLCPY
1524 strlcpy(char *dst, const char *src, size_t siz)
1526 register char *d = dst;
1527 register const char *s = src;
1528 register size_t n = siz;
1530 /* Copy as many bytes as will fit */
1531 if (n != 0 && --n != 0) {
1533 if ((*d++ = *s++) == 0)
1538 /* Not enough room in dst, add NUL and traverse rest of src */
1541 *d = '\0'; /* NUL-terminate dst */
1546 return(s - src - 1); /* count does not include NUL */
1549 #endif // #ifndef HAVE_STRLCPY
1551 void FindFraction(double val, int *num, int *denom, int denomMax)
1556 bestdiff = fabs(val);
1560 for(i = 1; i <= denomMax; ++i)
1562 int inum = (int) floor(0.5 + val * i);
1563 double diff = fabs(val - inum / (double)i);
1573 // decodes an XPM from C syntax
1574 char **XPM_DecodeString(const char *in)
1576 static char *tokens[257];
1577 static char lines[257][512];
1580 // skip until "{" token
1581 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1583 // now, read in succession: string, comma-or-}
1584 while(COM_ParseToken_QuakeC(&in, false))
1586 tokens[line] = lines[line];
1587 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1588 if(!COM_ParseToken_QuakeC(&in, false))
1590 if(!strcmp(com_token, "}"))
1592 if(strcmp(com_token, ","))
1594 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1601 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1602 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1604 unsigned char i0 = (bytes > 0) ? in[0] : 0;
1605 unsigned char i1 = (bytes > 1) ? in[1] : 0;
1606 unsigned char i2 = (bytes > 2) ? in[2] : 0;
1607 unsigned char o0 = base64[i0 >> 2];
1608 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1609 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1610 unsigned char o3 = base64[i2 & 077];
1611 out[0] = (bytes > 0) ? o0 : '?';
1612 out[1] = (bytes > 0) ? o1 : '?';
1613 out[2] = (bytes > 1) ? o2 : '=';
1614 out[3] = (bytes > 2) ? o3 : '=';
1617 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1620 // expand the out-buffer
1621 blocks = (buflen + 2) / 3;
1622 if(blocks*4 > outbuflen)
1624 for(i = blocks; i > 0; )
1627 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));