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;
867 Used by view and sv_user
870 float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval)
876 AngleVectors (angles, NULL, right, NULL);
877 side = DotProduct (velocity, right);
878 sign = side < 0 ? -1 : 1;
881 if (side < velocityval)
882 side = side * angleval / velocityval;
890 //===========================================================================
897 void COM_Init_Commands (void)
900 char com_cmdline[MAX_INPUTLINE];
902 Cvar_RegisterVariable (®istered);
903 Cvar_RegisterVariable (&cmdline);
904 Cvar_RegisterVariable(&cl_playermodel);
905 Cvar_RegisterAlias(&cl_playermodel, "_cl_playermodel");
906 Cvar_RegisterVariable(&cl_playerskin);
907 Cvar_RegisterAlias(&cl_playerskin, "_cl_playerskin");
909 // reconstitute the command line for the cmdline externally visible cvar
911 for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
914 if (strstr(sys.argv[j], " "))
916 // arg contains whitespace, store quotes around it
917 // This condition checks whether we can allow to put
918 // in two quote characters.
919 if (n >= ((int)sizeof(com_cmdline) - 2))
921 com_cmdline[n++] = '\"';
922 // This condition checks whether we can allow one
923 // more character and a quote character.
924 while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
925 // FIXME: Doesn't quote special characters.
926 com_cmdline[n++] = sys.argv[j][i++];
927 com_cmdline[n++] = '\"';
931 // This condition checks whether we can allow one
933 while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
934 com_cmdline[n++] = sys.argv[j][i++];
936 if (n < ((int)sizeof(com_cmdline) - 1))
937 com_cmdline[n++] = ' ';
942 Cvar_SetQuick(&cmdline, com_cmdline);
949 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
952 char *va(char *buf, size_t buflen, const char *format, ...)
956 va_start (argptr, format);
957 dpvsnprintf (buf, buflen, format,argptr);
964 //======================================
966 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
972 # define snprintf _snprintf
973 # define vsnprintf _vsnprintf
977 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
982 va_start (args, format);
983 result = dpvsnprintf (buffer, buffersize, format, args);
990 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
995 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
997 result = vsnprintf (buffer, buffersize, format, args);
999 if (result < 0 || (size_t)result >= buffersize)
1001 buffer[buffersize - 1] = '\0';
1009 //======================================
1011 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1016 if(utf8_enable.integer)
1019 while(*in && size_out > 1)
1022 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1023 ch = u8_tolower(ch);
1024 n = u8_fromchar(ch, out, size_out);
1033 while (*in && size_out > 1)
1035 if (*in >= 'A' && *in <= 'Z')
1036 *out++ = *in++ + 'a' - 'A';
1044 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1049 if(utf8_enable.integer)
1052 while(*in && size_out > 1)
1055 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1056 ch = u8_toupper(ch);
1057 n = u8_fromchar(ch, out, size_out);
1066 while (*in && size_out > 1)
1068 if (*in >= 'a' && *in <= 'z')
1069 *out++ = *in++ + 'A' - 'a';
1077 int COM_StringBeginsWith(const char *s, const char *match)
1079 for (;*s && *match;s++, match++)
1085 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1087 int argc, commentprefixlength;
1091 tokenbufend = tokenbuf + tokenbufsize;
1093 commentprefixlength = 0;
1095 commentprefixlength = (int)strlen(commentprefix);
1096 while (*l && *l != '\n' && *l != '\r')
1098 if (!ISWHITESPACE(*l))
1100 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1102 while (*l && *l != '\n' && *l != '\r')
1106 if (argc >= maxargc)
1108 argv[argc++] = tokenbuf;
1112 while (*l && *l != '"')
1114 if (tokenbuf >= tokenbufend)
1123 while (!ISWHITESPACE(*l))
1125 if (tokenbuf >= tokenbufend)
1130 if (tokenbuf >= tokenbufend)
1151 COM_StringLengthNoColors
1153 calculates the visible width of a color coded string.
1155 *valid is filled with TRUE if the string is a valid colored string (that is, if
1156 it does not end with an unfinished color code). If it gets filled with FALSE, a
1157 fix would be adding a STRING_COLOR_TAG at the end of the string.
1159 valid can be set to NULL if the caller doesn't care.
1161 For size_s, specify the maximum number of characters from s to use, or 0 to use
1162 all characters until the zero terminator.
1166 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1168 const char *end = size_s ? (s + size_s) : NULL;
1172 switch((s == end) ? 0 : *s)
1178 case STRING_COLOR_TAG:
1180 switch((s == end) ? 0 : *s)
1182 case STRING_COLOR_RGB_TAG_CHAR:
1183 if (s+1 != end && isxdigit(s[1]) &&
1184 s+2 != end && isxdigit(s[2]) &&
1185 s+3 != end && isxdigit(s[3]) )
1190 ++len; // STRING_COLOR_TAG
1191 ++len; // STRING_COLOR_RGB_TAG_CHAR
1193 case 0: // ends with unfinished color code!
1198 case STRING_COLOR_TAG: // escaped ^
1201 case '0': case '1': case '2': case '3': case '4':
1202 case '5': case '6': case '7': case '8': case '9': // color code
1204 default: // not a color code
1205 ++len; // STRING_COLOR_TAG
1206 ++len; // the character
1221 COM_StringDecolorize
1223 removes color codes from a string.
1225 If escape_carets is true, the resulting string will be safe for printing. If
1226 escape_carets is false, the function will just strip color codes (for logging
1229 If the output buffer size did not suffice for converting, the function returns
1230 FALSE. Generally, if escape_carets is false, the output buffer needs
1231 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1232 bytes. In any case, the function makes sure that the resulting string is
1235 For size_in, specify the maximum number of characters from in to use, or 0 to use
1236 all characters until the zero terminator.
1240 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1242 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1243 const char *end = size_in ? (in + size_in) : NULL;
1248 switch((in == end) ? 0 : *in)
1253 case STRING_COLOR_TAG:
1255 switch((in == end) ? 0 : *in)
1257 case STRING_COLOR_RGB_TAG_CHAR:
1258 if (in+1 != end && isxdigit(in[1]) &&
1259 in+2 != end && isxdigit(in[2]) &&
1260 in+3 != end && isxdigit(in[3]) )
1265 APPEND(STRING_COLOR_TAG);
1267 APPEND(STRING_COLOR_TAG);
1268 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1270 case 0: // ends with unfinished color code!
1271 APPEND(STRING_COLOR_TAG);
1272 // finish the code by appending another caret when escaping
1274 APPEND(STRING_COLOR_TAG);
1277 case STRING_COLOR_TAG: // escaped ^
1278 APPEND(STRING_COLOR_TAG);
1279 // append a ^ twice when escaping
1281 APPEND(STRING_COLOR_TAG);
1283 case '0': case '1': case '2': case '3': case '4':
1284 case '5': case '6': case '7': case '8': case '9': // color code
1286 default: // not a color code
1287 APPEND(STRING_COLOR_TAG);
1302 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1308 keylength = strlen(key);
1309 if (valuelength < 1 || !value)
1311 Con_Printf("InfoString_GetValue: no room in value\n");
1315 if (strchr(key, '\\'))
1317 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1320 if (strchr(key, '\"'))
1322 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1327 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1330 while (buffer[pos] == '\\')
1332 if (!memcmp(buffer + pos+1, key, keylength) &&
1333 (buffer[pos+1 + keylength] == 0 ||
1334 buffer[pos+1 + keylength] == '\\'))
1336 pos += 1 + (int)keylength; // Skip \key
1337 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1338 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1339 value[j] = buffer[pos+j];
1343 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1344 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1345 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1346 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1348 // if we reach this point the key was not found
1352 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1360 keylength = strlen(key);
1361 if (strchr(key, '\\') || strchr(value, '\\'))
1363 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1366 if (strchr(key, '\"') || strchr(value, '\"'))
1368 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1373 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1376 while (buffer[pos] == '\\')
1378 if (!memcmp(buffer + pos+1, key, keylength) &&
1379 (buffer[pos+1 + keylength] == 0 ||
1380 buffer[pos+1 + keylength] == '\\'))
1382 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1383 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1384 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1385 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1387 // if we found the key, find the end of it because we will be replacing it
1389 if (buffer[pos] == '\\')
1391 pos2 += 1 + (int)keylength; // Skip \key
1392 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1393 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1395 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1397 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1402 // set the key/value and append the remaining text
1403 char tempbuffer[MAX_INPUTLINE];
1404 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1405 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
1409 // just remove the key from the text
1410 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1414 void InfoString_Print(char *buffer)
1417 char key[MAX_INPUTLINE];
1418 char value[MAX_INPUTLINE];
1421 if (*buffer != '\\')
1423 Con_Printf("InfoString_Print: corrupt string\n");
1426 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1427 if (i < (int)sizeof(key)-1)
1430 if (*buffer != '\\')
1432 Con_Printf("InfoString_Print: corrupt string\n");
1435 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1436 if (i < (int)sizeof(value)-1)
1437 value[i++] = *buffer;
1439 // empty value is an error case
1440 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1444 //========================================================
1445 // strlcat and strlcpy, from OpenBSD
1448 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1450 * Permission to use, copy, modify, and distribute this software for any
1451 * purpose with or without fee is hereby granted, provided that the above
1452 * copyright notice and this permission notice appear in all copies.
1454 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1455 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1456 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1457 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1458 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1459 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1460 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1463 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
1464 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
1467 #ifndef HAVE_STRLCAT
1469 strlcat(char *dst, const char *src, size_t siz)
1471 register char *d = dst;
1472 register const char *s = src;
1473 register size_t n = siz;
1476 /* Find the end of dst and adjust bytes left but don't go past end */
1477 while (n-- != 0 && *d != '\0')
1483 return(dlen + strlen(s));
1484 while (*s != '\0') {
1493 return(dlen + (s - src)); /* count does not include NUL */
1495 #endif // #ifndef HAVE_STRLCAT
1498 #ifndef HAVE_STRLCPY
1500 strlcpy(char *dst, const char *src, size_t siz)
1502 register char *d = dst;
1503 register const char *s = src;
1504 register size_t n = siz;
1506 /* Copy as many bytes as will fit */
1507 if (n != 0 && --n != 0) {
1509 if ((*d++ = *s++) == 0)
1514 /* Not enough room in dst, add NUL and traverse rest of src */
1517 *d = '\0'; /* NUL-terminate dst */
1522 return(s - src - 1); /* count does not include NUL */
1525 #endif // #ifndef HAVE_STRLCPY
1527 void FindFraction(double val, int *num, int *denom, int denomMax)
1532 bestdiff = fabs(val);
1536 for(i = 1; i <= denomMax; ++i)
1538 int inum = (int) floor(0.5 + val * i);
1539 double diff = fabs(val - inum / (double)i);
1549 // decodes an XPM from C syntax
1550 char **XPM_DecodeString(const char *in)
1552 static char *tokens[257];
1553 static char lines[257][512];
1556 // skip until "{" token
1557 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1559 // now, read in succession: string, comma-or-}
1560 while(COM_ParseToken_QuakeC(&in, false))
1562 tokens[line] = lines[line];
1563 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1564 if(!COM_ParseToken_QuakeC(&in, false))
1566 if(!strcmp(com_token, "}"))
1568 if(strcmp(com_token, ","))
1570 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1577 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1578 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1580 unsigned char i0 = (bytes > 0) ? in[0] : 0;
1581 unsigned char i1 = (bytes > 1) ? in[1] : 0;
1582 unsigned char i2 = (bytes > 2) ? in[2] : 0;
1583 unsigned char o0 = base64[i0 >> 2];
1584 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1585 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1586 unsigned char o3 = base64[i2 & 077];
1587 out[0] = (bytes > 0) ? o0 : '?';
1588 out[1] = (bytes > 0) ? o1 : '?';
1589 out[2] = (bytes > 1) ? o2 : '=';
1590 out[3] = (bytes > 2) ? o3 : '=';
1593 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1596 // expand the out-buffer
1597 blocks = (buflen + 2) / 3;
1598 if(blocks*4 > outbuflen)
1600 for(i = blocks; i > 0; )
1603 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));