*/
// common.c -- misc functions used in client and server
-#include "quakedef.h"
-
#include <stdlib.h>
#include <fcntl.h>
#ifndef WIN32
#include <unistd.h>
#endif
-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"};
+#include "quakedef.h"
+#include "utf8lib.h"
+
+cvar_t registered = {CVAR_CLIENT | CVAR_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
+cvar_t cmdline = {CVAR_CLIENT | CVAR_SERVER, "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;
+const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the
const char *gamedirname1;
const char *gamedirname2;
const char *gamescreenshotname;
============================================================================
*/
-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
{
- 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;
+}
/*
============================================================================
MSG_WriteCoord32f (sb, f);
}
-void MSG_WriteVector (sizebuf_t *sb, float *v, protocolversion_t protocol)
+void MSG_WriteVector (sizebuf_t *sb, const vec3_t v, protocolversion_t protocol)
{
MSG_WriteCoord (sb, v[0], protocol);
MSG_WriteCoord (sb, v[1], protocol);
MSG_WriteCoord (sb, v[2], protocol);
}
-// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
+// LadyHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
void MSG_WriteAngle8i (sizebuf_t *sb, float f)
{
if (f >= 0)
//
// reading functions
//
-int msg_readcount;
-qboolean msg_badread;
-void MSG_BeginReading (void)
+void MSG_InitReadBuffer (sizebuf_t *buf, unsigned char *data, int size)
{
- msg_readcount = 0;
- msg_badread = false;
+ memset(buf, 0, sizeof(*buf));
+ buf->data = data;
+ buf->maxsize = buf->cursize = size;
+ MSG_BeginReading(buf);
}
-int MSG_ReadLittleShort (void)
+void MSG_BeginReading(sizebuf_t *sb)
{
- if (msg_readcount+2 > net_message.cursize)
+ sb->readcount = 0;
+ sb->badread = false;
+}
+
+int MSG_ReadLittleShort(sizebuf_t *sb)
+{
+ if (sb->readcount+2 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- msg_readcount += 2;
- return (short)(net_message.data[msg_readcount-2] | (net_message.data[msg_readcount-1]<<8));
+ sb->readcount += 2;
+ return (short)(sb->data[sb->readcount-2] | (sb->data[sb->readcount-1]<<8));
}
-int MSG_ReadBigShort (void)
+int MSG_ReadBigShort (sizebuf_t *sb)
{
- if (msg_readcount+2 > net_message.cursize)
+ if (sb->readcount+2 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- msg_readcount += 2;
- return (short)((net_message.data[msg_readcount-2]<<8) + net_message.data[msg_readcount-1]);
+ sb->readcount += 2;
+ return (short)((sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1]);
}
-int MSG_ReadLittleLong (void)
+int MSG_ReadLittleLong (sizebuf_t *sb)
{
- if (msg_readcount+4 > net_message.cursize)
+ if (sb->readcount+4 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- 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);
+ sb->readcount += 4;
+ return sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
}
-int MSG_ReadBigLong (void)
+int MSG_ReadBigLong (sizebuf_t *sb)
{
- if (msg_readcount+4 > net_message.cursize)
+ if (sb->readcount+4 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- 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];
+ sb->readcount += 4;
+ return (sb->data[sb->readcount-4]<<24) + (sb->data[sb->readcount-3]<<16) + (sb->data[sb->readcount-2]<<8) + sb->data[sb->readcount-1];
}
-float MSG_ReadLittleFloat (void)
+float MSG_ReadLittleFloat (sizebuf_t *sb)
{
union
{
float f;
int l;
} dat;
- if (msg_readcount+4 > net_message.cursize)
+ if (sb->readcount+4 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- 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);
+ sb->readcount += 4;
+ dat.l = sb->data[sb->readcount-4] | (sb->data[sb->readcount-3]<<8) | (sb->data[sb->readcount-2]<<16) | (sb->data[sb->readcount-1]<<24);
return dat.f;
}
-float MSG_ReadBigFloat (void)
+float MSG_ReadBigFloat (sizebuf_t *sb)
{
union
{
float f;
int l;
} dat;
- if (msg_readcount+4 > net_message.cursize)
+ if (sb->readcount+4 > sb->cursize)
{
- msg_badread = true;
+ sb->badread = true;
return -1;
}
- 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];
+ sb->readcount += 4;
+ dat.l = (sb->data[sb->readcount-4]<<24) | (sb->data[sb->readcount-3]<<16) | (sb->data[sb->readcount-2]<<8) | sb->data[sb->readcount-1];
return dat.f;
}
-char *MSG_ReadString (void)
+char *MSG_ReadString (sizebuf_t *sb, char *string, size_t maxstring)
{
- static char string[MAX_INPUTLINE];
- int l,c;
- for (l = 0;l < (int) sizeof(string) - 1 && (c = MSG_ReadByte()) != -1 && c != 0;l++)
- string[l] = c;
+ int c;
+ size_t l = 0;
+ // read string into sbfer, but only store as many characters as will fit
+ while ((c = MSG_ReadByte(sb)) > 0)
+ if (l < maxstring - 1)
+ string[l++] = c;
string[l] = 0;
return string;
}
-int MSG_ReadBytes (int numbytes, unsigned char *out)
+int MSG_ReadBytes (sizebuf_t *sb, int numbytes, unsigned char *out)
{
int l, c;
- for (l = 0;l < numbytes && (c = MSG_ReadByte()) != -1;l++)
+ for (l = 0;l < numbytes && (c = MSG_ReadByte(sb)) != -1;l++)
out[l] = c;
return l;
}
-float MSG_ReadCoord13i (void)
+float MSG_ReadCoord13i (sizebuf_t *sb)
{
- return MSG_ReadLittleShort() * (1.0/8.0);
+ return MSG_ReadLittleShort(sb) * (1.0/8.0);
}
-float MSG_ReadCoord16i (void)
+float MSG_ReadCoord16i (sizebuf_t *sb)
{
- return (signed short) MSG_ReadLittleShort();
+ return (signed short) MSG_ReadLittleShort(sb);
}
-float MSG_ReadCoord32f (void)
+float MSG_ReadCoord32f (sizebuf_t *sb)
{
- return MSG_ReadLittleFloat();
+ return MSG_ReadLittleFloat(sb);
}
-float MSG_ReadCoord (protocolversion_t protocol)
+float MSG_ReadCoord (sizebuf_t *sb, protocolversion_t protocol)
{
if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_QUAKEWORLD)
- return MSG_ReadCoord13i();
+ return MSG_ReadCoord13i(sb);
else if (protocol == PROTOCOL_DARKPLACES1)
- return MSG_ReadCoord32f();
+ return MSG_ReadCoord32f(sb);
else if (protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4)
- return MSG_ReadCoord16i();
+ return MSG_ReadCoord16i(sb);
else
- return MSG_ReadCoord32f();
+ return MSG_ReadCoord32f(sb);
}
-void MSG_ReadVector (float *v, protocolversion_t protocol)
+void MSG_ReadVector (sizebuf_t *sb, vec3_t v, protocolversion_t protocol)
{
- v[0] = MSG_ReadCoord(protocol);
- v[1] = MSG_ReadCoord(protocol);
- v[2] = MSG_ReadCoord(protocol);
+ v[0] = MSG_ReadCoord(sb, protocol);
+ v[1] = MSG_ReadCoord(sb, protocol);
+ v[2] = MSG_ReadCoord(sb, protocol);
}
-// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
-float MSG_ReadAngle8i (void)
+// LadyHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
+float MSG_ReadAngle8i (sizebuf_t *sb)
{
- return (signed char) MSG_ReadByte () * (360.0/256.0);
+ return (signed char) MSG_ReadByte (sb) * (360.0/256.0);
}
-float MSG_ReadAngle16i (void)
+float MSG_ReadAngle16i (sizebuf_t *sb)
{
- return (signed short)MSG_ReadShort () * (360.0/65536.0);
+ return (signed short)MSG_ReadShort (sb) * (360.0/65536.0);
}
-float MSG_ReadAngle32f (void)
+float MSG_ReadAngle32f (sizebuf_t *sb)
{
- return MSG_ReadFloat ();
+ return MSG_ReadFloat (sb);
}
-float MSG_ReadAngle (protocolversion_t protocol)
+float MSG_ReadAngle (sizebuf_t *sb, protocolversion_t protocol)
{
if (protocol == PROTOCOL_QUAKE || protocol == PROTOCOL_QUAKEDP || protocol == PROTOCOL_NEHAHRAMOVIE || protocol == PROTOCOL_NEHAHRABJP || protocol == PROTOCOL_NEHAHRABJP2 || protocol == PROTOCOL_NEHAHRABJP3 || protocol == PROTOCOL_DARKPLACES1 || protocol == PROTOCOL_DARKPLACES2 || protocol == PROTOCOL_DARKPLACES3 || protocol == PROTOCOL_DARKPLACES4 || protocol == PROTOCOL_QUAKEWORLD)
- return MSG_ReadAngle8i ();
+ return MSG_ReadAngle8i (sb);
else
- return MSG_ReadAngle16i ();
+ return MSG_ReadAngle16i (sb);
}
memcpy (SZ_GetSpace(buf,length),data,length);
}
-// LordHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
+// 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.
-static char *hexchar = "0123456789ABCDEF";
+static const char *hexchar = "0123456789ABCDEF";
void Com_HexDumpToConsole(const unsigned char *data, int size)
{
int i, j, n;
*cur++ = STRING_COLOR_TAG;
*cur++ = STRING_COLOR_TAG;
}
- else if (d[j] >= ' ')
+ else if (d[j] >= (unsigned char) ' ')
*cur++ = d[j];
else
*cur++ = '.';
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.
{
case 0: // end of string
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;
}
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
Parse a token out of a string
==============
*/
-int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash)
+int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
{
int len;
int c;
// 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] == '\r' && data[1] == '\n')
data++;
- if (data[0] == '/' && data[1] == '/')
+ if (parsecomments && data[0] == '/' && data[1] == '/')
{
// comment
while (*data && *data != '\n' && *data != '\r')
data++;
goto skipwhite;
}
- else if (data[0] == '/' && data[1] == '*')
+ else if (parsecomments && data[0] == '/' && data[1] == '*')
{
// comment
data++;
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;
// 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)
{
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;
// 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)
{
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;
// skip whitespace
skipwhite:
- for (;*data <= ' ';data++)
+ for (;ISWHITESPACE(*data);data++)
{
if (*data == 0)
{
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;
// Game mods
+gamemode_t com_startupgamemode;
+gamemode_t com_startupgamegroup;
+
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;
+ gamemode_t mode; // this gamemode
+ gamemode_t group; // different games with same group can switch automatically when gamedirs change
+ const char* prog_name; // not null
+ const char* cmdline; // not null
+ const char* gamename; // not null
+ const char* gamenetworkfiltername; // 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] =
-{// prog_name cmdline gamename basegame modgame screenshotprefix userdir
-
-// GAME_NORMAL
-// COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
-{ "", "-quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" },
-// GAME_HIPNOTIC
-// COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
-{ "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" },
-// GAME_ROGUE
-// COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
-{ "rogue", "-rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" },
-// GAME_NEHAHRA
-// COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
-{ "nehahra", "-nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" },
-// GAME_NEXUIZ
-// COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
-{ "nexuiz", "-nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" },
-// GAME_TRANSFUSION
-// COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
-{ "transfusion", "-transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" },
-// GAME_GOODVSBAD2
-// COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
-{ "gvb2", "-goodvsbad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" },
-// GAME_TEU
-// COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
-{ "teu", "-teu", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" },
-// GAME_BATTLEMECH
-// COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
-{ "battlemech", "-battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" },
-// GAME_ZYMOTIC
-// COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
-{ "zymotic", "-zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" },
-// GAME_SETHERAL
-// COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
-{ "setheral", "-setheral", "Setheral", "data", NULL, "setheral", "setheral" },
-// GAME_SOM
-// COMMANDLINEOPTION: Game: -som runs the multiplayer game Son Of Man
-{ "som", "-som", "Son of Man", "id1", "sonofman", "som", "darkplaces" },
-// GAME_TENEBRAE
-// COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
-{ "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" },
-// GAME_NEOTERIC
-// COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
-{ "neoteric", "-neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" },
-// GAME_OPENQUARTZ
-// COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
-{ "openquartz", "-openquartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" },
-// GAME_PRYDON
-// COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
-{ "prydon", "-prydon", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" },
-// GAME_DELUXEQUAKE
-// COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
-{ "dq", "-dq", "Deluxe Quake", "basedq", "extradq", "basedq", "dq" },
-// GAME_THEHUNTED
-// COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
-{ "thehunted", "-thehunted", "The Hunted", "thdata", NULL, "th", "thehunted" },
-// GAME_DEFEATINDETAIL2
-// COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
-{ "did2", "-did2", "Defeat In Detail 2", "data", NULL, "did2_", "did2" },
-// GAME_DARSANA
-// COMMANDLINEOPTION: Game: -darsana runs the game Darsana
-{ "darsana", "-darsana", "Darsana", "ddata", NULL, "darsana", "darsana" },
-// GAME_CONTAGIONTHEORY
-// COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
-{ "contagiontheory", "-contagiontheory", "Contagion Theory", "ctdata", NULL, "ct", "contagiontheory" },
-// 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 basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option
+{ GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
+{ GAME_HIPNOTIC, GAME_NORMAL, "hipnotic", "-hipnotic", "Darkplaces-Hipnotic", "Darkplaces-Hipnotic", "id1", "hipnotic", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -hipnotic runs Quake mission pack 1: The Scourge of Armagon
+{ GAME_ROGUE, GAME_NORMAL, "rogue", "-rogue", "Darkplaces-Rogue", "Darkplaces-Rogue", "id1", "rogue", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -rogue runs Quake mission pack 2: The Dissolution of Eternity
+{ GAME_NEHAHRA, GAME_NORMAL, "nehahra", "-nehahra", "DarkPlaces-Nehahra", "DarkPlaces-Nehahra", "id1", "nehahra", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -nehahra runs The Seal of Nehahra movie and game
+{ GAME_QUOTH, GAME_NORMAL, "quoth", "-quoth", "Darkplaces-Quoth", "Darkplaces-Quoth", "id1", "quoth", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quoth runs the Quoth mod for playing community maps made for it
+{ GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
+{ GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
+{ GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
+{ GAME_GOODVSBAD2, GAME_GOODVSBAD2, "gvb2", "-goodvsbad2", "GoodVs.Bad2", "GoodVs.Bad2", "rts", NULL, "gvb2", "gvb2" }, // COMMANDLINEOPTION: Game: -goodvsbad2 runs the psychadelic RTS FPS game Good Vs Bad 2
+{ GAME_TEU, GAME_TEU, "teu", "-teu", "TheEvilUnleashed", "TheEvilUnleashed", "baseteu", NULL, "teu", "teu" }, // COMMANDLINEOPTION: Game: -teu runs The Evil Unleashed (this option is obsolete as they are not using darkplaces)
+{ GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
+{ GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
+{ GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
+{ GAME_TENEBRAE, GAME_NORMAL, "tenebrae", "-tenebrae", "DarkPlaces-Tenebrae", "DarkPlaces-Tenebrae", "id1", "tenebrae", "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -tenebrae runs the graphics test mod known as Tenebrae (some features not implemented)
+{ GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
+{ GAME_OPENQUARTZ, GAME_NORMAL, "openquartz", "-openquartz", "OpenQuartz", "OpenQuartz", "id1", NULL, "openquartz", "darkplaces" }, // COMMANDLINEOPTION: Game: -openquartz runs the game OpenQuartz, a standalone GPL replacement of the quake content
+{ GAME_PRYDON, GAME_NORMAL, "prydon", "-prydon", "PrydonGate", "PrydonGate", "id1", "prydon", "prydon", "darkplaces" }, // COMMANDLINEOPTION: Game: -prydon runs the topdown point and click action-RPG Prydon Gate
+{ GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
+{ GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
+{ GAME_DEFEATINDETAIL2, GAME_DEFEATINDETAIL2, "did2", "-did2", "Defeat In Detail 2", "Defeat_In_Detail_2", "data", NULL, "did2_", "did2" }, // COMMANDLINEOPTION: Game: -did2 runs the game Defeat In Detail 2
+{ GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
+{ GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
+{ GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
+{ GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
+{ GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
+{ GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
+{ GAME_STEELSTORM2, GAME_STEELSTORM2, "steelstorm2", "-steelstorm2", "Steel Storm 2", "Steel_Storm_2", "gamedata", NULL, "ss2", "steelstorm2" }, // COMMANDLINEOPTION: Game: -steelstorm2 runs the game Steel Storm 2
+{ GAME_SSAMMO, GAME_SSAMMO, "steelstorm-ammo", "-steelstormammo", "Steel Storm A.M.M.O.", "Steel_Storm_A.M.M.O.", "gamedata", NULL, "ssammo", "steelstorm-ammo" }, // COMMANDLINEOPTION: Game: -steelstormammo runs the game Steel Storm A.M.M.O.
+{ GAME_STEELSTORMREVENANTS, GAME_STEELSTORMREVENANTS, "steelstorm-revenants", "-steelstormrev", "Steel Storm: Revenants", "Steel_Storm_Revenants", "base", NULL, "ssrev", "steelstorm-revenants" }, // COMMANDLINEOPTION: Game: -steelstormrev runs the game Steel Storm: Revenants
+{ GAME_TOMESOFMEPHISTOPHELES, GAME_TOMESOFMEPHISTOPHELES, "tomesofmephistopheles","-tomesofmephistopheles", "Tomes of Mephistopheles", "Tomes_of_Mephistopheles", "gamedata", NULL, "tom", "tomesofmephistopheles" }, // COMMANDLINEOPTION: Game: -tomesofmephistopheles runs the game Tomes of Mephistopheles
+{ GAME_STRAPBOMB, GAME_STRAPBOMB, "strapbomb", "-strapbomb", "Strap-on-bomb Car", "Strap-on-bomb_Car", "id1", NULL, "strap", "strapbomb" }, // COMMANDLINEOPTION: Game: -strapbomb runs the game Strap-on-bomb Car
+{ GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
+{ GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
};
+static void COM_SetGameType(int index);
void COM_InitGameType (void)
{
char name [MAX_OSPATH];
- unsigned int i;
+ int i;
+ int index = 0;
+
+#ifdef FORCEGAME
+ COM_ToLowerString(FORCEGAME, name, sizeof (name));
+#else
+ // check executable filename for keywords, but do it SMARTLY - only check the last path element
+ FS_StripExtension(FS_FileWithoutPath(com_argv[0]), name, sizeof (name));
+ COM_ToLowerString(name, name, sizeof (name));
+#endif
+ for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+ if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
+ index = i;
- FS_StripExtension (com_argv[0], name, sizeof (name));
- COM_ToLowerString (name, name, sizeof (name));
+ // check commandline options for keywords
+ for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+ if (COM_CheckParm (gamemode_info[i].cmdline))
+ index = i;
+
+ com_startupgamemode = gamemode_info[index].mode;
+ com_startupgamegroup = gamemode_info[index].group;
+ COM_SetGameType(index);
+}
- // Check the binary name; default to GAME_NORMAL (0)
- gamemode = GAME_NORMAL;
- for (i = 1; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
- if (strstr (name, gamemode_info[i].prog_name))
+void COM_ChangeGameTypeForGameDirs(void)
+{
+ int i;
+ int index = -1;
+ // this will not not change the gamegroup
+ // first check if a base game (single gamedir) matches
+ for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
+ {
+ if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
{
- gamemode = (gamemode_t)i;
+ index = i;
break;
}
-
- // Look for a command-line option
- for (i = 0; i < sizeof (gamemode_info) / sizeof (gamemode_info[0]); i++)
- if (COM_CheckParm (gamemode_info[i].cmdline))
+ }
+ // now that we have a base game, see if there is a matching derivative game (two gamedirs)
+ if (fs_numgamedirs)
+ {
+ for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
{
- gamemode = (gamemode_t)i;
- break;
+ if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
+ {
+ index = i;
+ break;
+ }
}
+ }
+ // we now have a good guess at which game this is meant to be...
+ if (index >= 0 && gamemode != gamemode_info[index].mode)
+ COM_SetGameType(index);
+}
+
+static void COM_SetGameType(int index)
+{
+ static char gamenetworkfilternamebuffer[64];
+ int i, t;
+ if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
+ index = 0;
+ gamemode = gamemode_info[index].mode;
+ gamename = gamemode_info[index].gamename;
+ gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
+ gamedirname1 = gamemode_info[index].gamedirname1;
+ gamedirname2 = gamemode_info[index].gamedirname2;
+ gamescreenshotname = gamemode_info[index].gamescreenshotname;
+ gameuserdirname = gamemode_info[index].gameuserdirname;
+
+ if (gamemode == com_startupgamemode)
+ {
+ if((t = COM_CheckParm("-customgamename")) && t + 1 < com_argc)
+ gamename = gamenetworkfiltername = com_argv[t+1];
+ if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < com_argc)
+ gamenetworkfiltername = 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];
+ }
+
+ if (gamedirname2 && gamedirname2[0])
+ Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
+ else
+ Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
+ for (i = 0;i < fs_numgamedirs;i++)
+ {
+ if (i == 0)
+ Con_Printf(", with mod gamedirs");
+ Con_Printf(" %s", fs_gamedirs[i]);
+ }
+ Con_Printf("\n");
- gamename = gamemode_info[gamemode].gamename;
- gamedirname1 = gamemode_info[gamemode].gamedirname1;
- gamedirname2 = gamemode_info[gamemode].gamedirname2;
- gamescreenshotname = gamemode_info[gamemode].gamescreenshotname;
- gameuserdirname = gamemode_info[gamemode].gameuserdirname;
+ if (strchr(gamenetworkfiltername, ' '))
+ {
+ char *s;
+ // if there are spaces in the game's network filter name it would
+ // cause parse errors in getservers in dpmaster, so we need to replace
+ // them with _ characters
+ strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
+ while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
+ *s = '_';
+ gamenetworkfiltername = gamenetworkfilternamebuffer;
+ }
+
+ Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
}
if (strstr(com_argv[j], " "))
{
// arg contains whitespace, store quotes around it
+ // This condition checks whether we can allow to put
+ // in two quote characters.
+ if (n >= ((int)sizeof(com_cmdline) - 2))
+ break;
com_cmdline[n++] = '\"';
- while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
+ // This condition checks whether we can allow one
+ // more character and a quote character.
+ while ((n < ((int)sizeof(com_cmdline) - 2)) && com_argv[j][i])
+ // FIXME: Doesn't quote special characters.
com_cmdline[n++] = com_argv[j][i++];
com_cmdline[n++] = '\"';
}
else
{
+ // This condition checks whether we can allow one
+ // more character.
while ((n < ((int)sizeof(com_cmdline) - 1)) && com_argv[j][i])
com_cmdline[n++] = com_argv[j][i++];
}
break;
}
com_cmdline[n] = 0;
- Cvar_Set ("cmdline", com_cmdline);
+ Cvar_SetQuick(&cmdline, com_cmdline);
}
/*
============
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
+varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
============
*/
-char *va(const char *format, ...)
+char *va(char *buf, size_t buflen, const char *format, ...)
{
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;
- s = string[stringindex];
- stringindex = (stringindex + 1) & 7;
va_start (argptr, format);
- dpvsnprintf (s, sizeof (string[0]), format,argptr);
+ dpvsnprintf (buf, buflen, format,argptr);
va_end (argptr);
- return s;
+ return buf;
}
{
int result;
+#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';
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')
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')
commentprefixlength = (int)strlen(commentprefix);
while (*l && *l != '\n' && *l != '\r')
{
- if (*l > ' ')
+ if (!ISWHITESPACE(*l))
{
if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
{
}
else
{
- while (*l > ' ')
+ while (!ISWHITESPACE(*l))
{
if (tokenbuf >= tokenbufend)
return -1;
++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)
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.
++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
#undef APPEND
}
-// written by Elric, thanks Elric!
-char *SearchInfostring(const char *infostring, const char *key)
-{
- static char value [MAX_INPUTLINE];
- char crt_key [MAX_INPUTLINE];
- size_t value_ind, key_ind;
- char c;
-
- if (*infostring++ != '\\')
- return NULL;
-
- value_ind = 0;
- for (;;)
- {
- key_ind = 0;
-
- // Get the key name
- for (;;)
- {
- c = *infostring++;
-
- if (c == '\0')
- return NULL;
- if (c == '\\' || key_ind == sizeof (crt_key) - 1)
- {
- crt_key[key_ind] = '\0';
- break;
- }
-
- crt_key[key_ind++] = c;
- }
-
- // If it's the key we are looking for, save it in "value"
- if (!strcmp(crt_key, key))
- {
- for (;;)
- {
- c = *infostring++;
-
- if (c == '\0' || c == '\\' || value_ind == sizeof (value) - 1)
- {
- value[value_ind] = '\0';
- return value;
- }
-
- value[value_ind++] = c;
- }
- }
-
- // Else, skip the value
- for (;;)
- {
- c = *infostring++;
-
- if (c == '\0')
- return NULL;
- if (c == '\\')
- break;
- }
- }
-}
-
-void InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
+char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
{
int pos = 0, j;
size_t keylength;
if (!key)
key = "";
- if (!value)
- value = "";
keylength = strlen(key);
if (valuelength < 1 || !value)
{
Con_Printf("InfoString_GetValue: no room in value\n");
- return;
+ return NULL;
}
value[0] = 0;
if (strchr(key, '\\'))
{
Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
- return;
+ return NULL;
}
if (strchr(key, '\"'))
{
Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
- return;
+ return NULL;
}
if (!key[0])
{
Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
- return;
+ return NULL;
}
while (buffer[pos] == '\\')
{
- if (!memcmp(buffer + pos+1, key, keylength))
+ if (!memcmp(buffer + pos+1, key, keylength) &&
+ (buffer[pos+1 + keylength] == 0 ||
+ buffer[pos+1 + keylength] == '\\'))
{
- for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
- pos++;
+ pos += 1 + (int)keylength; // Skip \key
+ if (buffer[pos] == '\\') pos++; // Skip \ before value.
for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
value[j] = buffer[pos+j];
value[j] = 0;
- return;
+ return value;
}
+ if (buffer[pos] == '\\') pos++; // Skip \ before value.
for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+ if (buffer[pos] == '\\') pos++; // Skip \ before value.
for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
}
// if we reach this point the key was not found
+ return NULL;
}
void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
}
while (buffer[pos] == '\\')
{
- if (!memcmp(buffer + pos+1, key, keylength))
+ if (!memcmp(buffer + pos+1, key, keylength) &&
+ (buffer[pos+1 + keylength] == 0 ||
+ buffer[pos+1 + keylength] == '\\'))
break;
- for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
- for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
+ if (buffer[pos] == '\\') pos++; // Skip \ before value.
+ for (;buffer[pos] && buffer[pos] != '\\';pos++);
+ if (buffer[pos] == '\\') pos++; // Skip \ before value.
+ for (;buffer[pos] && buffer[pos] != '\\';pos++);
}
// if we found the key, find the end of it because we will be replacing it
pos2 = pos;
if (buffer[pos] == '\\')
{
- for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
- for (pos2++;buffer[pos2] && buffer[pos2] != '\\';pos2++);
+ pos2 += 1 + (int)keylength; // Skip \key
+ if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
+ for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
}
if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
{
Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
return;
}
- if (value && value[0])
+ if (value[0])
{
// set the key/value and append the remaining text
- char tempbuffer[4096];
+ char tempbuffer[MAX_INPUTLINE];
strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
- sprintf(buffer + pos, "\\%s\\%s%s", key, value, tempbuffer);
+ dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
}
else
{
void InfoString_Print(char *buffer)
{
int i;
- char key[2048];
- char value[2048];
+ char key[MAX_INPUTLINE];
+ char value[MAX_INPUTLINE];
while (*buffer)
{
if (*buffer != '\\')
}
#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 : '=';
+}
+
+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;
+}