X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=common.c;h=8db1f27025ab61c545583f0f2cba1821ab41ff6d;hb=1a711173535d4b5f4364b66177745aab165330d8;hp=95043325e40be71f476bcb99c8220018dee5c347;hpb=49cfc77279303131ad6a3e3ed057f283970771b1;p=xonotic%2Fdarkplaces.git diff --git a/common.c b/common.c index 95043325..8db1f270 100644 --- a/common.c +++ b/common.c @@ -1,5 +1,6 @@ /* Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2000-2020 DarkPlaces contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -26,482 +27,668 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #include "quakedef.h" +#include "utf8lib.h" -cvar_t registered = {0, "registered","0"}; -cvar_t cmdline = {0, "cmdline","0"}; +cvar_t registered = {CF_CLIENT | CF_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"}; +cvar_t cmdline = {CF_CLIENT | CF_SERVER, "cmdline","0", "contains commandline the engine was launched with"}; -extern qboolean fs_modified; // set true if using non-id files +// FIXME: Find a better place for these. +cvar_t cl_playermodel = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playermodel", "", "current player model in Nexuiz/Xonotic"}; +cvar_t cl_playerskin = {CF_CLIENT | CF_SERVER | CF_USERINFO | CF_ARCHIVE, "playerskin", "", "current player skin in Nexuiz/Xonotic"}; -char com_token[1024]; -int com_argc; -const char **com_argv; +char com_token[MAX_INPUTLINE]; -// LordHavoc: made commandline 1024 characters instead of 256 -#define CMDLINE_LENGTH 1024 -char com_cmdline[CMDLINE_LENGTH]; - -int gamemode; -char *gamename; -char *gamedirname; -char com_modname[MAX_OSPATH]; - - -/* -============================================================================ - - BYTE ORDER FUNCTIONS - -============================================================================ -*/ - -#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) -short (*BigShort) (short l); -short (*LittleShort) (short l); -int (*BigLong) (int l); -int (*LittleLong) (int l); -float (*BigFloat) (float l); -float (*LittleFloat) (float l); -#endif - -short ShortSwap (short l) -{ - qbyte b1,b2; - - b1 = l&255; - b2 = (l>>8)&255; - - return (b1<<8) + b2; -} +//=========================================================================== -#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) -short ShortNoSwap (short l) +void SZ_Clear (sizebuf_t *buf) { - return l; + buf->cursize = 0; } -#endif -int LongSwap (int l) +unsigned char *SZ_GetSpace (sizebuf_t *buf, int length) { - qbyte b1,b2,b3,b4; - - b1 = l&255; - b2 = (l>>8)&255; - b3 = (l>>16)&255; - b4 = (l>>24)&255; + unsigned char *data; - return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; -} + if (buf->cursize + length > buf->maxsize) + { + if (!buf->allowoverflow) + Host_Error ("SZ_GetSpace: overflow without allowoverflow set"); -#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) -int LongNoSwap (int l) -{ - return l; -} -#endif + if (length > buf->maxsize) + Host_Error ("SZ_GetSpace: %i is > full buffer size", length); -float FloatSwap (float f) -{ - union - { - float f; - qbyte b[4]; - } dat1, dat2; + buf->overflowed = true; + Con_Print("SZ_GetSpace: overflow\n"); + SZ_Clear (buf); + } + data = buf->data + buf->cursize; + buf->cursize += length; - dat1.f = f; - dat2.b[0] = dat1.b[3]; - dat2.b[1] = dat1.b[2]; - dat2.b[2] = dat1.b[1]; - dat2.b[3] = dat1.b[0]; - return dat2.f; + return data; } -#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) -float FloatNoSwap (float f) +void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length) { - return f; + memcpy (SZ_GetSpace(buf,length),data,length); } -#endif +// LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my +// attention, it has been eradicated from here, its only (former) use in +// all of darkplaces. -// Extract integers from buffers - -unsigned int BuffBigLong (const qbyte *buffer) -{ - return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; -} - -unsigned short BuffBigShort (const qbyte *buffer) +static const char *hexchar = "0123456789ABCDEF"; +void Com_HexDumpToConsole(const unsigned char *data, int size) { - return (buffer[0] << 8) | buffer[1]; + int i, j, n; + char text[1024]; + char *cur, *flushpointer; + const unsigned char *d; + cur = text; + flushpointer = text + 512; + for (i = 0;i < size;) + { + n = 16; + if (n > size - i) + n = size - i; + d = data + i; + // print offset + *cur++ = hexchar[(i >> 12) & 15]; + *cur++ = hexchar[(i >> 8) & 15]; + *cur++ = hexchar[(i >> 4) & 15]; + *cur++ = hexchar[(i >> 0) & 15]; + *cur++ = ':'; + // print hex + for (j = 0;j < 16;j++) + { + if (j < n) + { + *cur++ = hexchar[(d[j] >> 4) & 15]; + *cur++ = hexchar[(d[j] >> 0) & 15]; + } + else + { + *cur++ = ' '; + *cur++ = ' '; + } + if ((j & 3) == 3) + *cur++ = ' '; + } + // print text + for (j = 0;j < 16;j++) + { + if (j < n) + { + // color change prefix character has to be treated specially + if (d[j] == STRING_COLOR_TAG) + { + *cur++ = STRING_COLOR_TAG; + *cur++ = STRING_COLOR_TAG; + } + else if (d[j] >= (unsigned char) ' ') + *cur++ = d[j]; + else + *cur++ = '.'; + } + else + *cur++ = ' '; + } + *cur++ = '\n'; + i += n; + if (cur >= flushpointer || i >= size) + { + *cur++ = 0; + Con_Print(text); + cur = text; + } + } } -unsigned int BuffLittleLong (const qbyte *buffer) +void SZ_HexDumpToConsole(const sizebuf_t *buf) { - return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; + Com_HexDumpToConsole(buf->data, buf->cursize); } -unsigned short BuffLittleShort (const qbyte *buffer) -{ - return (buffer[1] << 8) | buffer[0]; -} +//============================================================================ /* -============================================================================== +============== +COM_Wordwrap - MESSAGE IO FUNCTIONS +Word wraps a string. The wordWidth function is guaranteed to be called exactly +once for each word in the string, so it may be stateful, no idea what that +would be good for any more. At the beginning of the string, it will be called +for the char 0 to initialize a clean state, and then once with the string " " +(a space) so the routine knows how long a space is. -Handles byte ordering and avoids alignment errors -============================================================================== -*/ +In case no single character fits into the given width, the wordWidth function +must return the width of exactly one character. -// -// writing functions -// +Wrapped lines get the isContinuation flag set and are continuationWidth less wide. -void MSG_WriteChar (sizebuf_t *sb, int c) +The sum of the return values of the processLine function will be returned. +============== +*/ +int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL) { - qbyte *buf; - - buf = SZ_GetSpace (sb, 1); - buf[0] = c; -} + // Logic is as follows: + // + // For each word or whitespace: + // Newline found? Output current line, advance to next line. This is not a continuation. Continue. + // Space found? Always add it to the current line, no matter if it fits. + // Word found? Check if current line + current word fits. + // If it fits, append it. Continue. + // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue. + + qbool isContinuation = false; + float spaceWidth; + const char *startOfLine = string; + const char *cursor = string; + const char *end = string + length; + float spaceUsedInLine = 0; + float spaceUsedForWord; + int result = 0; + size_t wordLen; + size_t dummy; + + dummy = 0; + wordWidth(passthroughCW, NULL, &dummy, -1); + dummy = 1; + spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1); + + for(;;) + { + char ch = (cursor < end) ? *cursor : 0; + switch(ch) + { + case 0: // end of string + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + goto out; + case '\n': // end of line + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + isContinuation = false; + ++cursor; + startOfLine = cursor; + break; + case ' ': // space + ++cursor; + spaceUsedInLine += spaceWidth; + break; + default: // word + wordLen = 1; + while(cursor + wordLen < end) + { + switch(cursor[wordLen]) + { + case 0: + case '\n': + case ' ': + goto out_inner; + default: + ++wordLen; + break; + } + } + out_inner: + 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 + if(wordLen < 1) // cannot happen according to current spec of wordWidth + { + wordLen = 1; + spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself + } + if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine) + { + // we can simply append it + cursor += wordLen; + spaceUsedInLine += spaceUsedForWord; + } + else + { + // output current line + result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation); + isContinuation = true; + startOfLine = cursor; + cursor += wordLen; + spaceUsedInLine = continuationWidth + spaceUsedForWord; + } + } + } + out: -void MSG_WriteByte (sizebuf_t *sb, int c) -{ - qbyte *buf; + return result; - buf = SZ_GetSpace (sb, 1); - buf[0] = c; -} +/* + qbool isContinuation = false; + float currentWordSpace = 0; + const char *currentWord = 0; + float minReserve = 0; -void MSG_WriteShort (sizebuf_t *sb, int c) -{ - qbyte *buf; + float spaceUsedInLine = 0; + const char *currentLine = 0; + const char *currentLineEnd = 0; + float currentLineFinalWhitespace = 0; + const char *p; - buf = SZ_GetSpace (sb, 2); - buf[0] = c&0xff; - buf[1] = c>>8; -} + int result = 0; + minReserve = charWidth(passthroughCW, 0); + minReserve += charWidth(passthroughCW, ' '); -void MSG_WriteLong (sizebuf_t *sb, int c) -{ - qbyte *buf; + if(maxWidth < continuationWidth + minReserve) + maxWidth = continuationWidth + minReserve; - buf = SZ_GetSpace (sb, 4); - buf[0] = c&0xff; - buf[1] = (c>>8)&0xff; - buf[2] = (c>>16)&0xff; - buf[3] = c>>24; -} + charWidth(passthroughCW, 0); -void MSG_WriteFloat (sizebuf_t *sb, float f) -{ - union + for(p = string; p < string + length; ++p) { - float f; - int l; - } dat; + char c = *p; + float w = charWidth(passthroughCW, c); + if(!currentWord) + { + currentWord = p; + currentWordSpace = 0; + } - dat.f = f; - dat.l = LittleLong (dat.l); + if(!currentLine) + { + currentLine = p; + spaceUsedInLine = isContinuation ? continuationWidth : 0; + currentLineEnd = 0; + } - SZ_Write (sb, &dat.l, 4); -} + if(c == ' ') + { + // 1. I can add the word AND a space - then just append it. + if(spaceUsedInLine + currentWordSpace + w <= maxWidth) + { + currentLineEnd = p; // note: space not included here + currentLineFinalWhitespace = w; + spaceUsedInLine += currentWordSpace + w; + } + // 2. I can just add the word - then append it, output current line and go to next one. + else if(spaceUsedInLine + currentWordSpace <= maxWidth) + { + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + currentLine = 0; + isContinuation = true; + } + // 3. Otherwise, output current line and go to next one, where I can add the word. + else if(continuationWidth + currentWordSpace + w <= maxWidth) + { + if(currentLineEnd) + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + currentLine = currentWord; + spaceUsedInLine = continuationWidth + currentWordSpace + w; + currentLineEnd = p; + currentLineFinalWhitespace = w; + isContinuation = true; + } + // 4. We can't even do that? Then output both current and next word as new lines. + else + { + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + currentLine = 0; + isContinuation = true; + } + currentWord = 0; + } + else if(c == '\n') + { + // 1. I can add the word - then do it. + if(spaceUsedInLine + currentWordSpace <= maxWidth) + { + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + } + // 2. Otherwise, output current line, next one and make tabula rasa. + else + { + if(currentLineEnd) + { + processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + } + currentWord = 0; + currentLine = 0; + isContinuation = false; + } + else + { + currentWordSpace += w; + if( + spaceUsedInLine + currentWordSpace > maxWidth // can't join this line... + && + continuationWidth + currentWordSpace > maxWidth // can't join any other line... + ) + { + // this word cannot join ANY line... + // so output the current line... + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } -void MSG_WriteString (sizebuf_t *sb, const char *s) -{ - if (!s) - SZ_Write (sb, "", 1); - else - SZ_Write (sb, s, strlen(s)+1); -} + // then this word's beginning... + if(isContinuation) + { + // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces + float pieceWidth = maxWidth - continuationWidth; + const char *pos = currentWord; + currentWordSpace = 0; + + // reset the char width function to a state where no kerning occurs (start of word) + charWidth(passthroughCW, ' '); + while(pos <= p) + { + float w = charWidth(passthroughCW, *pos); + if(currentWordSpace + w > pieceWidth) // this piece won't fit any more + { + // print everything until it + result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true); + // go to here + currentWord = pos; + currentWordSpace = 0; + } + currentWordSpace += w; + ++pos; + } + // now we have a currentWord that fits... set up its next line + // currentWordSpace has been set + // currentWord has been set + spaceUsedInLine = continuationWidth; + currentLine = currentWord; + currentLineEnd = 0; + isContinuation = true; + } + else + { + // we have a guarantee that it will fix (see if clause) + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation); + + // and use the rest of this word as new start of a line + currentWordSpace = w; + currentWord = p; + spaceUsedInLine = continuationWidth; + currentLine = p; + currentLineEnd = 0; + isContinuation = true; + } + } + } + } -// used by server (always latest PROTOCOL_DARKPLACES) -void MSG_WriteDPCoord (sizebuf_t *sb, float f) -{ - if (f >= 0) - MSG_WriteShort (sb, (int)(f + 0.5f)); - else - MSG_WriteShort (sb, (int)(f - 0.5f)); -} + if(!currentWord) + { + currentWord = p; + currentWordSpace = 0; + } -void MSG_WritePreciseAngle (sizebuf_t *sb, float f) -{ - if (f >= 0) - MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535); - else - MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535); -} + if(currentLine) // Same procedure as \n + { + // Can I append the current word? + if(spaceUsedInLine + currentWordSpace <= maxWidth) + result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation); + else + { + if(currentLineEnd) + { + result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation); + isContinuation = true; + } + result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation); + } + } -// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem -void MSG_WriteAngle (sizebuf_t *sb, float f) -{ - if (f >= 0) - MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255); - else - MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255); + return result; +*/ } -// -// reading functions -// -int msg_readcount; -qboolean msg_badread; +/* +============== +COM_ParseToken_Simple -void MSG_BeginReading (void) +Parse a token out of a string +============== +*/ +int COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments) { - msg_readcount = 0; - msg_badread = false; -} + int len; + int c; + const char *data = *datapointer; -int MSG_ReadLittleShort (void) -{ - if (msg_readcount+2 > net_message.cursize) - { - msg_badread = true; - return -1; - } - msg_readcount += 2; - return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8)); -} + len = 0; + com_token[0] = 0; -int MSG_ReadBigShort (void) -{ - if (msg_readcount+2 > net_message.cursize) + if (!data) { - msg_badread = true; - return -1; + *datapointer = NULL; + return false; } - msg_readcount += 2; - return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]); -} -int MSG_ReadLittleLong (void) -{ - if (msg_readcount+4 > net_message.cursize) +// skip whitespace +skipwhite: + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { - msg_badread = true; - return -1; + if (*data == 0) + { + // end of file + *datapointer = NULL; + return false; + } } - msg_readcount += 4; - 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); -} -int MSG_ReadBigLong (void) -{ - if (msg_readcount+4 > net_message.cursize) + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + + if (parsecomments && data[0] == '/' && data[1] == '/') { - msg_badread = true; - return -1; + // comment + while (*data && *data != '\n' && *data != '\r') + data++; + goto skipwhite; } - msg_readcount += 4; - 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]; -} - -float MSG_ReadLittleFloat (void) -{ - union + else if (parsecomments && data[0] == '/' && data[1] == '*') { - float f; - int l; - } dat; - if (msg_readcount+4 > net_message.cursize) + // comment + data++; + while (*data && (data[0] != '*' || data[1] != '/')) + data++; + if (*data) + data++; + if (*data) + data++; + goto skipwhite; + } + else if (*data == '\"') { - msg_badread = true; - return -1; + // quoted string + for (data++;*data && *data != '\"';data++) + { + c = *data; + if (*data == '\\' && parsebackslash) + { + data++; + c = *data; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + } + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = c; + } + com_token[len] = 0; + if (*data == '\"') + data++; + *datapointer = data; + return true; } - msg_readcount += 4; - 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); - return dat.f; -} - -float MSG_ReadBigFloat (void) -{ - union + else if (*data == '\r') { - float f; - int l; - } dat; - if (msg_readcount+4 > net_message.cursize) + // translate Mac line ending to UNIX + com_token[len++] = '\n';data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else if (*data == '\n') { - msg_badread = true; - return -1; + // single character + com_token[len++] = *data++; + com_token[len] = 0; + *datapointer = data; + return true; } - msg_readcount += 4; - 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]; - return dat.f; -} - -char *MSG_ReadString (void) -{ - static char string[2048]; - int l,c; - for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadChar()) != -1 && c != 0;l++) - string[l] = c; - string[l] = 0; - return string; -} - -int MSG_ReadBytes (int numbytes, unsigned char *out) -{ - int l, c; - for (l = 0;l < numbytes && (c = MSG_ReadChar()) != -1;l++) - out[l] = c; - return l; -} - -// used by server (always latest PROTOCOL_DARKPLACES) -float MSG_ReadDPCoord (void) -{ - return (signed short) MSG_ReadLittleShort(); -} - -// used by client -float MSG_ReadCoord (void) -{ - if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3 || cl.protocol == PROTOCOL_DARKPLACES4) - return (signed short) MSG_ReadLittleShort(); - else if (cl.protocol == PROTOCOL_DARKPLACES1) - return MSG_ReadLittleFloat(); else - return MSG_ReadLittleShort() * (1.0f/8.0f); -} - - -//=========================================================================== - -void SZ_Alloc (sizebuf_t *buf, int startsize, const char *name) -{ - if (startsize < 256) - startsize = 256; - buf->mempool = Mem_AllocPool(name); - buf->data = Mem_Alloc(buf->mempool, startsize); - buf->maxsize = startsize; - buf->cursize = 0; + { + // regular word + for (;!ISWHITESPACE(*data);data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; + com_token[len] = 0; + *datapointer = data; + return true; + } } +/* +============== +COM_ParseToken_QuakeC -void SZ_Free (sizebuf_t *buf) -{ - Mem_FreePool(&buf->mempool); - buf->data = NULL; - buf->maxsize = 0; - buf->cursize = 0; -} - -void SZ_Clear (sizebuf_t *buf) +Parse a token out of a string +============== +*/ +int COM_ParseToken_QuakeC(const char **datapointer, qbool returnnewline) { - buf->cursize = 0; -} + int len; + int c; + const char *data = *datapointer; -void *SZ_GetSpace (sizebuf_t *buf, int length) -{ - void *data; + len = 0; + com_token[0] = 0; - if (buf->cursize + length > buf->maxsize) + if (!data) { - if (!buf->allowoverflow) - Host_Error ("SZ_GetSpace: overflow without allowoverflow set\n"); - - if (length > buf->maxsize) - Host_Error ("SZ_GetSpace: %i is > full buffer size\n", length); - - buf->overflowed = true; - Con_Printf ("SZ_GetSpace: overflow\n"); - SZ_Clear (buf); + *datapointer = NULL; + return false; } - data = buf->data + buf->cursize; - buf->cursize += length; - - return data; -} - -void SZ_Write (sizebuf_t *buf, const void *data, int length) -{ - memcpy (SZ_GetSpace(buf,length),data,length); -} +// skip whitespace +skipwhite: + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) + { + if (*data == 0) + { + // end of file + *datapointer = NULL; + return false; + } + } -// LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my -// attention, it has been eradicated from here, its only (former) use in -// all of darkplaces. + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; -static char *hexchar = "0123456789ABCDEF"; -void Com_HexDumpToConsole(const qbyte *data, int size) -{ - int i, j, n; - char text[1024]; - char *cur, *flushpointer; - const qbyte *d; - cur = text; - flushpointer = text + 512; - for (i = 0;i < size;) + if (data[0] == '/' && data[1] == '/') { - n = 16; - if (n > size - i) - n = size - i; - d = data + i; - *cur++ = hexchar[(i >> 12) & 15]; - *cur++ = hexchar[(i >> 8) & 15]; - *cur++ = hexchar[(i >> 4) & 15]; - *cur++ = hexchar[(i >> 0) & 15]; - *cur++ = ':'; - for (j = 0;j < n;j++) + // comment + while (*data && *data != '\n' && *data != '\r') + data++; + goto skipwhite; + } + else if (data[0] == '/' && data[1] == '*') + { + // comment + data++; + while (*data && (data[0] != '*' || data[1] != '/')) + data++; + if (*data) + data++; + if (*data) + data++; + goto skipwhite; + } + else if (*data == '\"' || *data == '\'') + { + // quoted string + char quote = *data; + for (data++;*data && *data != quote;data++) { - if (j & 1) + c = *data; + if (*data == '\\') { - *cur++ = hexchar[(d[j] >> 4) & 15] | 0x80; - *cur++ = hexchar[(d[j] >> 0) & 15] | 0x80; - } - else - { - *cur++ = hexchar[(d[j] >> 4) & 15]; - *cur++ = hexchar[(d[j] >> 0) & 15]; + data++; + c = *data; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; } + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = c; } - for (;j < 16;j++) - { - *cur++ = ' '; - *cur++ = ' '; - } - for (j = 0;j < n;j++) - *cur++ = (d[j] >= ' ' && d[j] <= 0x7E) ? d[j] : '.'; - for (;j < 16;j++) - *cur++ = ' '; - *cur++ = '\n'; - i += n; - if (cur >= flushpointer || i >= size) - { - *cur++ = 0; - Con_Printf("%s", text); - cur = text; - } + com_token[len] = 0; + if (*data == quote) + data++; + *datapointer = data; + return true; + } + else if (*data == '\r') + { + // translate Mac line ending to UNIX + com_token[len++] = '\n';data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';') + { + // single character + com_token[len++] = *data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else + { + // regular word + for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; + com_token[len] = 0; + *datapointer = data; + return true; } } -void SZ_HexDumpToConsole(const sizebuf_t *buf) -{ - Com_HexDumpToConsole(buf->data, buf->cursize); -} - - -//============================================================================ - - /* ============== -COM_ParseToken +COM_ParseToken_VM_Tokenize Parse a token out of a string ============== */ -int COM_ParseToken(const char **datapointer, int returnnewline) +int COM_ParseToken_VM_Tokenize(const char **datapointer, qbool returnnewline) { - int c; int len; + int c; const char *data = *datapointer; len = 0; @@ -515,91 +702,105 @@ int COM_ParseToken(const char **datapointer, int returnnewline) // skip whitespace skipwhite: - while ((c = *data) <= ' ' && (c != '\n' || !returnnewline)) + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++) { - if (c == 0) + if (*data == 0) { // end of file *datapointer = NULL; return false; } - data++; } - // check if it's a comment - if (c == '/') + // handle Windows line ending + if (data[0] == '\r' && data[1] == '\n') + data++; + + if (data[0] == '/' && data[1] == '/') { - // skip // comments - if (data[1] == '/') - { - while (*data && *data != '\n') - data++; - goto skipwhite; - } - // skip /* comments - if (data[1] == '*') - { - while (*data && (*data != '*' || data[1] != '/')) - data++; - data+=2; - goto skipwhite; - } + // comment + while (*data && *data != '\n' && *data != '\r') + data++; + goto skipwhite; } - -// handle quoted strings specially - if (c == '\"') + else if (data[0] == '/' && data[1] == '*') { + // comment data++; - while (1) + while (*data && (data[0] != '*' || data[1] != '/')) + data++; + if (*data) + data++; + if (*data) + data++; + goto skipwhite; + } + else if (*data == '\"' || *data == '\'') + { + char quote = *data; + // quoted string + for (data++;*data && *data != quote;data++) { - c = *data++; - if (c == '\"' || !c) + c = *data; + if (*data == '\\') { - com_token[len] = 0; - *datapointer = data; - return true; + data++; + c = *data; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; } - com_token[len] = c; - len++; + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = c; } + com_token[len] = 0; + if (*data == quote) + data++; + *datapointer = data; + return true; } - -// parse single characters - if (c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';' || c == '\n') + else if (*data == '\r') { - com_token[len] = c; - len++; + // translate Mac line ending to UNIX + com_token[len++] = '\n';data++; com_token[len] = 0; - *datapointer = data+1; + *datapointer = data; return true; } - -// parse a regular word - do + else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';') { - com_token[len] = c; - data++; - len++; - c = *data; - if (c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\'' || c == ':' || c == ',' || c == ';') - break; - } while (c>32); - - com_token[len] = 0; - *datapointer = data; - return true; + // single character + com_token[len++] = *data++; + com_token[len] = 0; + *datapointer = data; + return true; + } + else + { + // regular word + for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; + com_token[len] = 0; + *datapointer = data; + return true; + } } /* ============== -COM_ParseTokenConsole +COM_ParseToken_Console Parse a token out of a string, behaving like the qwcl console ============== */ -int COM_ParseTokenConsole(const char **datapointer) +int COM_ParseToken_Console(const char **datapointer) { - int c; int len; const char *data = *datapointer; @@ -614,323 +815,195 @@ int COM_ParseTokenConsole(const char **datapointer) // skip whitespace skipwhite: - while ((c = *data) <= ' ') + for (;ISWHITESPACE(*data);data++) { - if (c == 0) + if (*data == 0) { // end of file *datapointer = NULL; return false; } - data++; } - // skip // comments - if (c == '/' && data[1] == '/') + if (*data == '/' && data[1] == '/') { - while (*data && *data != '\n') + // comment + while (*data && *data != '\n' && *data != '\r') data++; goto skipwhite; } - -// handle quoted strings specially - if (c == '\"') + else if (*data == '\"') { - data++; - while (1) + // quoted string + for (data++;*data && *data != '\"';data++) { - c = *data++; - if (c == '\"' || !c) - { - com_token[len] = 0; - *datapointer = data; - return true; - } - com_token[len] = c; - len++; + // allow escaped " and \ case + if (*data == '\\' && (data[1] == '\"' || data[1] == '\\')) + data++; + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; } + com_token[len] = 0; + if (*data == '\"') + data++; + *datapointer = data; } - -// parse a regular word - do + else { - com_token[len] = c; - data++; - len++; - c = *data; - } while (c>32); + // regular word + for (;!ISWHITESPACE(*data);data++) + if (len < (int)sizeof(com_token) - 1) + com_token[len++] = *data; + com_token[len] = 0; + *datapointer = data; + } - com_token[len] = 0; - *datapointer = data; return true; } - /* -================ -COM_CheckParm +=============== +Com_CalcRoll -Returns the position (1 to argc-1) in the program's argument list -where the given parameter apears, or 0 if not present -================ +Used by view and sv_user +=============== */ -int COM_CheckParm (const char *parm) +float Com_CalcRoll (const vec3_t angles, const vec3_t velocity, const vec_t angleval, const vec_t velocityval) { - int i; - - for (i=1 ; i= ((int)sizeof(com_cmdline) - 2)) + break; + com_cmdline[n++] = '\"'; + // This condition checks whether we can allow one + // more character and a quote character. + while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i]) + // FIXME: Doesn't quote special characters. + com_cmdline[n++] = sys.argv[j][i++]; + com_cmdline[n++] = '\"'; + } + else + { + // This condition checks whether we can allow one + // more character. + while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i]) + com_cmdline[n++] = sys.argv[j][i++]; + } + if (n < ((int)sizeof(com_cmdline) - 1)) com_cmdline[n++] = ' '; else break; } com_cmdline[n] = 0; + Cvar_SetQuick(&cmdline, com_cmdline); } -void COM_InitGameType (void) +/* +============ +va + +varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf) +============ +*/ +char *va(char *buf, size_t buflen, const char *format, ...) { - char name[MAX_OSPATH]; - FS_StripExtension (com_argv[0], name, sizeof (name)); - COM_ToLowerString (name, name, sizeof (name)); - - if (strstr(name, "transfusion")) - gamemode = GAME_TRANSFUSION; - else if (strstr(name, "nexuiz")) - gamemode = GAME_NEXUIZ; - else if (strstr(name, "nehahra")) - gamemode = GAME_NEHAHRA; - else if (strstr(name, "hipnotic")) - gamemode = GAME_HIPNOTIC; - else if (strstr(name, "rogue")) - gamemode = GAME_ROGUE; - else if (strstr(name, "gvb2")) - gamemode = GAME_GOODVSBAD2; - else if (strstr(name, "teu")) - gamemode = GAME_TEU; - else if (strstr(name, "battlemech")) - gamemode = GAME_BATTLEMECH; - else if (strstr(name, "zymotic")) - gamemode = GAME_ZYMOTIC; - else if (strstr(name, "fniggium")) - gamemode = GAME_FNIGGIUM; - else if (strstr(name, "setheral")) - gamemode = GAME_SETHERAL; - else if (strstr(name, "som")) - gamemode = GAME_SOM; - else - gamemode = GAME_NORMAL; - - if (COM_CheckParm ("-transfusion")) - gamemode = GAME_TRANSFUSION; - else if (COM_CheckParm ("-nexuiz")) - gamemode = GAME_NEXUIZ; - else if (COM_CheckParm ("-nehahra")) - gamemode = GAME_NEHAHRA; - else if (COM_CheckParm ("-hipnotic")) - gamemode = GAME_HIPNOTIC; - else if (COM_CheckParm ("-rogue")) - gamemode = GAME_ROGUE; - else if (COM_CheckParm ("-quake")) - gamemode = GAME_NORMAL; - else if (COM_CheckParm ("-goodvsbad2")) - gamemode = GAME_GOODVSBAD2; - else if (COM_CheckParm ("-teu")) - gamemode = GAME_TEU; - else if (COM_CheckParm ("-battlemech")) - gamemode = GAME_BATTLEMECH; - else if (COM_CheckParm ("-zymotic")) - gamemode = GAME_ZYMOTIC; - else if (COM_CheckParm ("-fniggium")) - gamemode = GAME_FNIGGIUM; - else if (COM_CheckParm ("-setheral")) - gamemode = GAME_SETHERAL; - else if (COM_CheckParm ("-som")) - gamemode = GAME_SOM; - - switch(gamemode) - { - case GAME_NORMAL: - gamename = "DarkPlaces-Quake"; - gamedirname = ""; - break; - case GAME_HIPNOTIC: - gamename = "Darkplaces-Hipnotic"; - gamedirname = "hipnotic"; - break; - case GAME_ROGUE: - gamename = "Darkplaces-Rogue"; - gamedirname = "rogue"; - break; - case GAME_NEHAHRA: - gamename = "DarkPlaces-Nehahra"; - gamedirname = "nehahra"; - break; - case GAME_NEXUIZ: - gamename = "Nexuiz"; - gamedirname = "data"; - break; - case GAME_TRANSFUSION: - gamename = "Transfusion"; - gamedirname = "transfusion"; - break; - case GAME_GOODVSBAD2: - gamename = "GoodVs.Bad2"; - gamedirname = "rts"; - break; - case GAME_TEU: - gamename = "TheEvilUnleashed"; - gamedirname = "baseteu"; - break; - case GAME_BATTLEMECH: - gamename = "Battlemech"; - gamedirname = "base"; - break; - case GAME_ZYMOTIC: - gamename = "Zymotic"; - gamedirname = "data"; - break; - case GAME_FNIGGIUM: - gamename = "Fniggium"; - gamedirname = "data"; - break; - case GAME_SETHERAL: - gamename = "Setheral"; - gamedirname = "data"; - break; - case GAME_SOM: - gamename = "Son of Man"; - gamedirname = "data"; - break; - default: - Sys_Error("COM_InitGameType: unknown gamemode %i\n", gamemode); - break; - } + va_list argptr; + + va_start (argptr, format); + dpvsnprintf (buf, buflen, format,argptr); + va_end (argptr); + + return buf; } -extern void Mathlib_Init(void); -extern void FS_Init (void); +//====================================== + +// snprintf and vsnprintf are NOT portable. Use their DP counterparts instead -/* -================ -COM_Init -================ -*/ -void COM_Init (void) -{ -#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG) - qbyte swaptest[2] = {1,0}; +#undef snprintf +#undef vsnprintf -// set the byte swapping variables in a portable manner - if ( *(short *)swaptest == 1) - { - BigShort = ShortSwap; - LittleShort = ShortNoSwap; - BigLong = LongSwap; - LittleLong = LongNoSwap; - BigFloat = FloatSwap; - LittleFloat = FloatNoSwap; - } - else - { - BigShort = ShortNoSwap; - LittleShort = ShortSwap; - BigLong = LongNoSwap; - LittleLong = LongSwap; - BigFloat = FloatNoSwap; - LittleFloat = FloatSwap; - } +#ifdef WIN32 +# define snprintf _snprintf +# define vsnprintf _vsnprintf #endif - Cvar_RegisterVariable (®istered); - Cvar_RegisterVariable (&cmdline); - Mathlib_Init(); +int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...) +{ + va_list args; + int result; - FS_Init (); - Con_InitLogging(); - COM_CheckRegistered (); + va_start (args, format); + result = dpvsnprintf (buffer, buffersize, format, args); + va_end (args); - COM_InitGameType(); + return result; } -/* -============ -va - -does a varargs printf into a temp buffer, so I don't need to have -varargs versions of all text functions. -FIXME: make this buffer size safe someday -============ -*/ -char *va(const char *format, ...) +int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args) { - va_list argptr; - // LordHavoc: now cycles through 8 buffers to avoid problems in most cases - static char string[8][1024], *s; - static int stringindex = 0; + int result; - s = string[stringindex]; - stringindex = (stringindex + 1) & 7; - va_start (argptr, format); - vsnprintf (s, sizeof (string[0]), format,argptr); - va_end (argptr); +#if _MSC_VER >= 1400 + result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args); +#else + result = vsnprintf (buffer, buffersize, format, args); +#endif + if (result < 0 || (size_t)result >= buffersize) + { + buffer[buffersize - 1] = '\0'; + return -1; + } - return s; + return result; } @@ -941,6 +1014,23 @@ void COM_ToLowerString (const char *in, char *out, size_t size_out) if (size_out == 0) return; + if(utf8_enable.integer) + { + *out = 0; + while(*in && size_out > 1) + { + int n; + Uchar ch = u8_getchar_utf8_enabled(in, &in); + ch = u8_tolower(ch); + n = u8_fromchar(ch, out, size_out); + if(n <= 0) + break; + out += n; + size_out -= n; + } + return; + } + while (*in && size_out > 1) { if (*in >= 'A' && *in <= 'Z') @@ -957,6 +1047,23 @@ void COM_ToUpperString (const char *in, char *out, size_t size_out) if (size_out == 0) return; + if(utf8_enable.integer) + { + *out = 0; + while(*in && size_out > 1) + { + int n; + Uchar ch = u8_getchar_utf8_enabled(in, &in); + ch = u8_toupper(ch); + n = u8_fromchar(ch, out, size_out); + if(n <= 0) + break; + out += n; + size_out -= n; + } + return; + } + while (*in && size_out > 1) { if (*in >= 'a' && *in <= 'z') @@ -986,14 +1093,14 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t l = *text; commentprefixlength = 0; if (commentprefix) - commentprefixlength = strlen(commentprefix); - while (*l && *l != '\n') + commentprefixlength = (int)strlen(commentprefix); + while (*l && *l != '\n' && *l != '\r') { - if (*l > ' ') + if (!ISWHITESPACE(*l)) { if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength)) { - while (*l && *l != '\n') + while (*l && *l != '\n' && *l != '\r') l++; break; } @@ -1014,7 +1121,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t } else { - while (*l > ' ') + while (!ISWHITESPACE(*l)) { if (tokenbuf >= tokenbufend) return -1; @@ -1028,80 +1135,176 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t else l++; } + // line endings: + // UNIX: \n + // Mac: \r + // Windows: \r\n + if (*l == '\r') + l++; if (*l == '\n') l++; *text = l; return argc; } -// written by Elric, thanks Elric! -char *SearchInfostring(const char *infostring, const char *key) -{ - static char value [256]; - char crt_key [256]; - size_t value_ind, key_ind; - char c; +/* +============ +COM_StringLengthNoColors - if (*infostring++ != '\\') - return NULL; +calculates the visible width of a color coded string. - value_ind = 0; - for (;;) - { - key_ind = 0; +*valid is filled with TRUE if the string is a valid colored string (that is, if +it does not end with an unfinished color code). If it gets filled with FALSE, a +fix would be adding a STRING_COLOR_TAG at the end of the string. - // Get the key name - for (;;) - { - c = *infostring++; +valid can be set to NULL if the caller doesn't care. - if (c == '\0') - return NULL; - if (c == '\\' || key_ind == sizeof (crt_key) - 1) - { - crt_key[key_ind] = '\0'; +For size_s, specify the maximum number of characters from s to use, or 0 to use +all characters until the zero terminator. +============ +*/ +size_t +COM_StringLengthNoColors(const char *s, size_t size_s, qbool *valid) +{ + const char *end = size_s ? (s + size_s) : NULL; + size_t len = 0; + for(;;) + { + switch((s == end) ? 0 : *s) + { + case 0: + if(valid) + *valid = true; + return len; + case STRING_COLOR_TAG: + ++s; + switch((s == end) ? 0 : *s) + { + case STRING_COLOR_RGB_TAG_CHAR: + if (s+1 != end && isxdigit(s[1]) && + s+2 != end && isxdigit(s[2]) && + s+3 != end && isxdigit(s[3]) ) + { + s+=3; + break; + } + ++len; // STRING_COLOR_TAG + ++len; // STRING_COLOR_RGB_TAG_CHAR + break; + case 0: // ends with unfinished color code! + ++len; + if(valid) + *valid = false; + return len; + case STRING_COLOR_TAG: // escaped ^ + ++len; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': // color code + break; + default: // not a color code + ++len; // STRING_COLOR_TAG + ++len; // the character + break; + } + break; + default: + ++len; break; - } - - crt_key[key_ind++] = c; } + ++s; + } + // never get here +} - // If it's the key we are looking for, save it in "value" - if (!strcmp(crt_key, key)) - { - for (;;) - { - c = *infostring++; +/* +============ +COM_StringDecolorize - if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1) - { - value[value_ind] = '\0'; - return value; - } +removes color codes from a string. - value[value_ind++] = c; - } - } +If escape_carets is true, the resulting string will be safe for printing. If +escape_carets is false, the function will just strip color codes (for logging +for example). - // Else, skip the value - for (;;) - { - c = *infostring++; +If the output buffer size did not suffice for converting, the function returns +FALSE. Generally, if escape_carets is false, the output buffer needs +strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2 +bytes. In any case, the function makes sure that the resulting string is +zero terminated. - if (c == '\0') - return NULL; - if (c == '\\') +For size_in, specify the maximum number of characters from in to use, or 0 to use +all characters until the zero terminator. +============ +*/ +qbool +COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qbool escape_carets) +{ +#define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0) + const char *end = size_in ? (in + size_in) : NULL; + if(size_out < 1) + return false; + for(;;) + { + switch((in == end) ? 0 : *in) + { + case 0: + *out++ = 0; + return true; + case STRING_COLOR_TAG: + ++in; + switch((in == end) ? 0 : *in) + { + case STRING_COLOR_RGB_TAG_CHAR: + if (in+1 != end && isxdigit(in[1]) && + in+2 != end && isxdigit(in[2]) && + in+3 != end && isxdigit(in[3]) ) + { + in+=3; + break; + } + APPEND(STRING_COLOR_TAG); + if(escape_carets) + APPEND(STRING_COLOR_TAG); + APPEND(STRING_COLOR_RGB_TAG_CHAR); + break; + case 0: // ends with unfinished color code! + APPEND(STRING_COLOR_TAG); + // finish the code by appending another caret when escaping + if(escape_carets) + APPEND(STRING_COLOR_TAG); + *out++ = 0; + return true; + case STRING_COLOR_TAG: // escaped ^ + APPEND(STRING_COLOR_TAG); + // append a ^ twice when escaping + if(escape_carets) + APPEND(STRING_COLOR_TAG); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': // color code + break; + default: // not a color code + APPEND(STRING_COLOR_TAG); + APPEND(*in); + break; + } + break; + default: + APPEND(*in); break; } + ++in; } + // never get here +#undef APPEND } - //======================================================== // strlcat and strlcpy, from OpenBSD /* - * Copyright (c) 1998 Todd C. Miller + * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1116,65 +1319,146 @@ char *SearchInfostring(const char *infostring, const char *key) * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */ -/* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */ +/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ +/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ -// Most (all?) BSDs already have them -#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !(defined(__APPLE__) && defined(__MACH__)) - +#ifndef HAVE_STRLCAT size_t -strlcat(char *dst, const char *src, size_t siz) +strlcat(char *dst, const char *src, size_t dsize) { - register char *d = dst; - register const char *s = src; - register size_t n = siz; + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; size_t dlen; - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; n--; } - s++; + src++; } - *d = '\0'; + *dst = '\0'; - return(dlen + (s - src)); /* count does not include NUL */ + return(dlen + (src - osrc)); /* count does not include NUL */ } +#endif // #ifndef HAVE_STRLCAT + +#ifndef HAVE_STRLCPY size_t -strlcpy(char *dst, const char *src, size_t siz) +strlcpy(char *dst, const char *src, size_t dsize) { - register char *d = dst; - register const char *s = src; - register size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') break; - } while (--n != 0); + } } - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) ; } - return(s - src - 1); /* count does not include NUL */ + return(src - osrc - 1); /* count does not include NUL */ +} + +#endif // #ifndef HAVE_STRLCPY + +void FindFraction(double val, int *num, int *denom, int denomMax) +{ + int i; + double bestdiff; + // initialize + bestdiff = fabs(val); + *num = 0; + *denom = 1; + + for(i = 1; i <= denomMax; ++i) + { + int inum = (int) floor(0.5 + val * i); + double diff = fabs(val - inum / (double)i); + if(diff < bestdiff) + { + bestdiff = diff; + *num = inum; + *denom = i; + } + } +} + +// decodes an XPM from C syntax +char **XPM_DecodeString(const char *in) +{ + static char *tokens[257]; + static char lines[257][512]; + size_t line = 0; + + // skip until "{" token + while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{")); + + // now, read in succession: string, comma-or-} + while(COM_ParseToken_QuakeC(&in, false)) + { + tokens[line] = lines[line]; + strlcpy(lines[line++], com_token, sizeof(lines[0])); + if(!COM_ParseToken_QuakeC(&in, false)) + return NULL; + if(!strcmp(com_token, "}")) + break; + if(strcmp(com_token, ",")) + return NULL; + if(line >= sizeof(tokens) / sizeof(tokens[0])) + return NULL; + } + + return tokens; +} + +static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes) +{ + unsigned char i0 = (bytes > 0) ? in[0] : 0; + unsigned char i1 = (bytes > 1) ? in[1] : 0; + unsigned char i2 = (bytes > 2) ? in[2] : 0; + unsigned char o0 = base64[i0 >> 2]; + unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077]; + unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077]; + unsigned char o3 = base64[i2 & 077]; + out[0] = (bytes > 0) ? o0 : '?'; + out[1] = (bytes > 0) ? o1 : '?'; + out[2] = (bytes > 1) ? o2 : '='; + out[3] = (bytes > 2) ? o3 : '='; } -#endif // #if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) +size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen) +{ + size_t blocks, i; + // expand the out-buffer + blocks = (buflen + 2) / 3; + if(blocks*4 > outbuflen) + return 0; + for(i = blocks; i > 0; ) + { + --i; + base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i)); + } + return blocks * 4; +}