]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - common.c
refactored and optimized the R_Q1BSP_RecursiveGetLightInfo function
[xonotic/darkplaces.git] / common.c
index 499dccfd49f3ab7a9ca8e956e0982da6c92a3683..71c059aca7d739bb5452ce701c45dd432e51412f 100644 (file)
--- a/common.c
+++ b/common.c
@@ -19,20 +19,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // common.c -- misc functions used in client and server
 
 */
 // common.c -- misc functions used in client and server
 
-#include "quakedef.h"
-
 #include <stdlib.h>
 #include <fcntl.h>
 #ifndef WIN32
 #include <unistd.h>
 #endif
 
 #include <stdlib.h>
 #include <fcntl.h>
 #ifndef WIN32
 #include <unistd.h>
 #endif
 
+#include "quakedef.h"
+
 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
 
 char com_token[MAX_INPUTLINE];
 int com_argc;
 const char **com_argv;
 cvar_t registered = {0, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
 cvar_t cmdline = {0, "cmdline","0", "contains commandline the engine was launched with"};
 
 char com_token[MAX_INPUTLINE];
 int com_argc;
 const char **com_argv;
+int com_selffd = -1;
 
 gamemode_t gamemode;
 const char *gamename;
 
 gamemode_t gamemode;
 const char *gamename;
@@ -51,68 +52,78 @@ char com_modname[MAX_OSPATH] = "";
 ============================================================================
 */
 
 ============================================================================
 */
 
-short   ShortSwap (short l)
-{
-       unsigned char    b1,b2;
 
 
-       b1 = l&255;
-       b2 = (l>>8)&255;
-
-       return (b1<<8) + b2;
+float BuffBigFloat (const unsigned char *buffer)
+{
+       union
+       {
+               float f;
+               unsigned int i;
+       }
+       u;
+       u.i = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       return u.f;
 }
 
 }
 
-int    LongSwap (int l)
+int BuffBigLong (const unsigned char *buffer)
 {
 {
-       unsigned char    b1,b2,b3,b4;
-
-       b1 = l&255;
-       b2 = (l>>8)&255;
-       b3 = (l>>16)&255;
-       b4 = (l>>24)&255;
+       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+}
 
 
-       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+short BuffBigShort (const unsigned char *buffer)
+{
+       return (buffer[0] << 8) | buffer[1];
 }
 
 }
 
-float FloatSwap (float f)
+float BuffLittleFloat (const unsigned char *buffer)
 {
        union
        {
 {
        union
        {
-               float   f;
-               unsigned char    b[4];
-       } dat1, dat2;
-
-
-       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;
+               float f;
+               unsigned int i;
+       }
+       u;
+       u.i = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       return u.f;
 }
 
 }
 
+int BuffLittleLong (const unsigned char *buffer)
+{
+       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+}
 
 
-// Extract integers from buffers
-
-unsigned int BuffBigLong (const unsigned char *buffer)
+short BuffLittleShort (const unsigned char *buffer)
 {
 {
-       return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+       return (buffer[1] << 8) | buffer[0];
 }
 
 }
 
-unsigned short BuffBigShort (const unsigned char *buffer)
+void StoreBigLong (unsigned char *buffer, unsigned int i)
 {
 {
-       return (buffer[0] << 8) | buffer[1];
+       buffer[0] = (i >> 24) & 0xFF;
+       buffer[1] = (i >> 16) & 0xFF;
+       buffer[2] = (i >>  8) & 0xFF;
+       buffer[3] = i         & 0xFF;
 }
 
 }
 
-unsigned int BuffLittleLong (const unsigned char *buffer)
+void StoreBigShort (unsigned char *buffer, unsigned short i)
 {
 {
-       return (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
+       buffer[0] = (i >>  8) & 0xFF;
+       buffer[1] = i         & 0xFF;
 }
 
 }
 
-unsigned short BuffLittleShort (const unsigned char *buffer)
+void StoreLittleLong (unsigned char *buffer, unsigned int i)
 {
 {
-       return (buffer[1] << 8) | buffer[0];
+       buffer[0] = i         & 0xFF;
+       buffer[1] = (i >>  8) & 0xFF;
+       buffer[2] = (i >> 16) & 0xFF;
+       buffer[3] = (i >> 24) & 0xFF;
 }
 
 }
 
+void StoreLittleShort (unsigned char *buffer, unsigned short i)
+{
+       buffer[0] = i         & 0xFF;
+       buffer[1] = (i >>  8) & 0xFF;
+}
 
 /*
 ============================================================================
 
 /*
 ============================================================================
@@ -595,7 +606,7 @@ void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
 // attention, it has been eradicated from here, its only (former) use in
 // all of darkplaces.
 
 // attention, it has been eradicated from here, its only (former) use in
 // all of darkplaces.
 
-static char *hexchar = "0123456789ABCDEF";
+static const char *hexchar = "0123456789ABCDEF";
 void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
        int i, j, n;
 void Com_HexDumpToConsole(const unsigned char *data, int size)
 {
        int i, j, n;
@@ -643,7 +654,7 @@ void Com_HexDumpToConsole(const unsigned char *data, int size)
                                        *cur++ = STRING_COLOR_TAG;
                                        *cur++ = STRING_COLOR_TAG;
                                }
                                        *cur++ = STRING_COLOR_TAG;
                                        *cur++ = STRING_COLOR_TAG;
                                }
-                               else if (d[j] >= ' ')
+                               else if (d[j] >= (unsigned char) ' ')
                                        *cur++ = d[j];
                                else
                                        *cur++ = '.';
                                        *cur++ = d[j];
                                else
                                        *cur++ = '.';
@@ -680,6 +691,9 @@ 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.
 
 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.
 
+In case no single character fits into the given width, the wordWidth function
+must return the width of exactly one character.
+
 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
 
 The sum of the return values of the processLine function will be returned.
 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
 
 The sum of the return values of the processLine function will be returned.
@@ -721,7 +735,6 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                goto out;
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                                goto out;
-                               break;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
                        case '\n': // end of line
                                result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
                                isContinuation = false;
@@ -749,7 +762,7 @@ int COM_Wordwrap(const char *string, size_t length, float continuationWidth, flo
                                }
                                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
                                }
                                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)
+                               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
                                {
                                        wordLen = 1;
                                        spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
@@ -996,7 +1009,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1072,7 +1085,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ';data++)
+               for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1109,7 +1122,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1186,7 +1199,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
+               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;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1223,7 +1236,7 @@ skipwhite:
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
        // UNIX: \n
        // Mac: \r
        // Windows: \r\n
-       for (;*data <= ' ' && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
+       for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1300,7 +1313,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ' && *data != ',' && *data != ';' && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
+               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;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1332,7 +1345,7 @@ int COM_ParseToken_Console(const char **datapointer)
 
 // skip whitespace
 skipwhite:
 
 // skip whitespace
 skipwhite:
-       for (;*data <= ' ';data++)
+       for (;ISWHITESPACE(*data);data++)
        {
                if (*data == 0)
                {
        {
                if (*data == 0)
                {
@@ -1368,7 +1381,7 @@ skipwhite:
        else
        {
                // regular word
        else
        {
                // regular word
-               for (;*data > ' ';data++)
+               for (;!ISWHITESPACE(*data);data++)
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
                        if (len < (int)sizeof(com_token) - 1)
                                com_token[len++] = *data;
                com_token[len] = 0;
@@ -1408,13 +1421,13 @@ int COM_CheckParm (const char *parm)
 
 typedef struct gamemode_info_s
 {
 
 typedef struct gamemode_info_s
 {
-       const char* prog_name;
-       const char* cmdline;
-       const char* gamename;
-       const char* gamedirname1;
-       const char* gamedirname2;
-       const char* gamescreenshotname;
-       const char* gameuserdirname;
+       const char* prog_name; // not null
+       const char* cmdline; // not null
+       const char* gamename; // not null
+       const char* gamedirname1; // not null
+       const char* gamedirname2; // null
+       const char* gamescreenshotname; // not nul
+       const char* gameuserdirname; // not null
 } gamemode_info_t;
 
 static const gamemode_info_t gamemode_info [GAME_COUNT] =
 } gamemode_info_t;
 
 static const gamemode_info_t gamemode_info [GAME_COUNT] =
@@ -1435,6 +1448,9 @@ static const gamemode_info_t gamemode_info [GAME_COUNT] =
 // GAME_NEXUIZ
 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
 { "nexuiz",                    "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
 // GAME_NEXUIZ
 // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
 { "nexuiz",                    "-nexuiz",              "Nexuiz",                               "data",         NULL,                   "nexuiz",               "nexuiz" },
+// GAME_XONOTIC
+// COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
+{ "xonotic",                   "-xonotic",             "Xonotic",                              "data",         NULL,                   "xonotic",              "xonotic" },
 // GAME_TRANSFUSION
 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
 { "transfusion",       "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
 // GAME_TRANSFUSION
 // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
 { "transfusion",       "-transfusion", "Transfusion",                  "basetf",       NULL,                   "transfusion",  "transfusion" },
@@ -1486,18 +1502,28 @@ static const gamemode_info_t gamemode_info [GAME_COUNT] =
 // GAME_EDU2P
 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
 // GAME_EDU2P
 // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
 { "edu2p", "-edu2p", "EDU2 Prototype", "id1", "edu2", "edu2_p", "edu2prototype" },
-// GAME_BLADEMASTER
-// COMMANDLINEOPTION: Game: -blademaster runs the game Prophecy: Return of the BladeMaster
-{ "blademaster", "-blademaster", "Prophecy: Return of the BladeMaster", "basebm", NULL, "blademaster", "blademaster" },
 // GAME_PROPHECY
 // GAME_PROPHECY
-// COMMANDLINEOPTION: Game: -prophecy runs the game Quake (default)
+// COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
 { "prophecy",                          "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
 { "prophecy",                          "-prophecy",            "Prophecy",             "data",         NULL,                   "prophecy",                     "prophecy" },
+// GAME_BLOODOMNICIDE
+// COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
+{ "omnicide", "-omnicide", "Blood Omnicide", "kain", NULL, "omnicide", "omnicide" },
+// GAME_STEELSTORM
+// COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ "steelstorm",                                "-steelstorm",          "Steel-Storm",          "gamedata",             NULL,                   "ss",                   "steelstorm" },
+// GAME_STRAPBOMB
+// COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
+{ "strapbomb",                         "-strapbomb",           "Strap-on-bomb Car",            "id1",          NULL,                   "strap",                        "strapbomb" },
+// GAME_MOONHELM
+// COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
+{ "moonhelm",                          "-moonhelm",            "MoonHelm",             "data",         NULL,                   "mh",                   "moonhelm" },
 };
 
 void COM_InitGameType (void)
 {
        char name [MAX_OSPATH];
        unsigned int i;
 };
 
 void COM_InitGameType (void)
 {
        char name [MAX_OSPATH];
        unsigned int i;
+       int t;
 
        FS_StripExtension (com_argv[0], name, sizeof (name));
        COM_ToLowerString (name, name, sizeof (name));
 
        FS_StripExtension (com_argv[0], name, sizeof (name));
        COM_ToLowerString (name, name, sizeof (name));
@@ -1524,6 +1550,17 @@ void COM_InitGameType (void)
        gamedirname2 = gamemode_info[gamemode].gamedirname2;
        gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
        gameuserdirname = gamemode_info[gamemode].gameuserdirname;
        gamedirname2 = gamemode_info[gamemode].gamedirname2;
        gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
        gameuserdirname = gamemode_info[gamemode].gameuserdirname;
+
+       if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
+               gamename = com_argv[t+1];
+       if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < com_argc)
+               gamedirname1 = com_argv[t+1];
+       if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < com_argc)
+               gamedirname2 = *com_argv[t+1] ? com_argv[t+1] : NULL;
+       if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < com_argc)
+               gamescreenshotname = com_argv[t+1];
+       if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < com_argc)
+               gameuserdirname = com_argv[t+1];
 }
 
 
 }
 
 
@@ -1693,7 +1730,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                commentprefixlength = (int)strlen(commentprefix);
        while (*l && *l != '\n' && *l != '\r')
        {
                commentprefixlength = (int)strlen(commentprefix);
        while (*l && *l != '\n' && *l != '\r')
        {
-               if (*l > ' ')
+               if (!ISWHITESPACE(*l))
                {
                        if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
                        {
                {
                        if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
                        {
@@ -1718,7 +1755,7 @@ int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *t
                        }
                        else
                        {
                        }
                        else
                        {
-                               while (*l > ' ')
+                               while (!ISWHITESPACE(*l))
                                {
                                        if (tokenbuf >= tokenbufend)
                                                return -1;
                                {
                                        if (tokenbuf >= tokenbufend)
                                                return -1;
@@ -1777,6 +1814,17 @@ COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
                                ++s;
                                switch((s == end) ? 0 : *s)
                                {
                                ++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)
                                        case 0: // ends with unfinished color code!
                                                ++len;
                                                if(valid)
@@ -1815,7 +1863,7 @@ for example).
 
 If the output buffer size did not suffice for converting, the function returns
 FALSE. Generally, if escape_carets is false, the output buffer needs
 
 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)+2
+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.
 
 bytes. In any case, the function makes sure that the resulting string is
 zero terminated.
 
@@ -1841,6 +1889,19 @@ COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out,
                                ++in;
                                switch((in == end) ? 0 : *in)
                                {
                                ++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
                                        case 0: // ends with unfinished color code!
                                                APPEND(STRING_COLOR_TAG);
                                                // finish the code by appending another caret when escaping
@@ -1941,8 +2002,6 @@ void InfoString_GetValue(const char *buffer, const char *key, char *value, size_
        size_t keylength;
        if (!key)
                key = "";
        size_t keylength;
        if (!key)
                key = "";
-       if (!value)
-               value = "";
        keylength = strlen(key);
        if (valuelength < 1 || !value)
        {
        keylength = strlen(key);
        if (valuelength < 1 || !value)
        {
@@ -2030,7 +2089,7 @@ void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, con
                // set the key/value and append the remaining text
                char tempbuffer[4096];
                strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
                // set the key/value and append the remaining text
                char tempbuffer[4096];
                strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
-               dpsnprintf(buffer + pos, sizeof(buffer) - pos, "\\%s\\%s%s", key, value, tempbuffer);
+               dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
        }
        else
        {
        }
        else
        {
@@ -2151,3 +2210,53 @@ strlcpy(char *dst, const char *src, size_t siz)
 }
 
 #endif  // #ifndef HAVE_STRLCPY
 }
 
 #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;
+}