2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // common.c -- misc functions used in client and server
31 cvar_t registered = {CVAR_CLIENT | CVAR_SERVER, "registered","0", "indicates if this is running registered quake (whether gfx/pop.lmp was found)"};
32 cvar_t cmdline = {CVAR_CLIENT | CVAR_SERVER, "cmdline","0", "contains commandline the engine was launched with"};
34 char com_token[MAX_INPUTLINE];
38 const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the
39 const char *gamedirname1;
40 const char *gamedirname2;
41 const char *gamescreenshotname;
42 const char *gameuserdirname;
43 char com_modname[MAX_OSPATH] = "";
45 //===========================================================================
47 void SZ_Clear (sizebuf_t *buf)
52 unsigned char *SZ_GetSpace (sizebuf_t *buf, int length)
56 if (buf->cursize + length > buf->maxsize)
58 if (!buf->allowoverflow)
59 Host_Error ("SZ_GetSpace: overflow without allowoverflow set");
61 if (length > buf->maxsize)
62 Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
64 buf->overflowed = true;
65 Con_Print("SZ_GetSpace: overflow\n");
69 data = buf->data + buf->cursize;
70 buf->cursize += length;
75 void SZ_Write (sizebuf_t *buf, const unsigned char *data, int length)
77 memcpy (SZ_GetSpace(buf,length),data,length);
80 // LadyHavoc: thanks to Fuh for bringing the pure evil of SZ_Print to my
81 // attention, it has been eradicated from here, its only (former) use in
84 static const char *hexchar = "0123456789ABCDEF";
85 void Com_HexDumpToConsole(const unsigned char *data, int size)
89 char *cur, *flushpointer;
90 const unsigned char *d;
92 flushpointer = text + 512;
100 *cur++ = hexchar[(i >> 12) & 15];
101 *cur++ = hexchar[(i >> 8) & 15];
102 *cur++ = hexchar[(i >> 4) & 15];
103 *cur++ = hexchar[(i >> 0) & 15];
106 for (j = 0;j < 16;j++)
110 *cur++ = hexchar[(d[j] >> 4) & 15];
111 *cur++ = hexchar[(d[j] >> 0) & 15];
122 for (j = 0;j < 16;j++)
126 // color change prefix character has to be treated specially
127 if (d[j] == STRING_COLOR_TAG)
129 *cur++ = STRING_COLOR_TAG;
130 *cur++ = STRING_COLOR_TAG;
132 else if (d[j] >= (unsigned char) ' ')
142 if (cur >= flushpointer || i >= size)
151 void SZ_HexDumpToConsole(const sizebuf_t *buf)
153 Com_HexDumpToConsole(buf->data, buf->cursize);
157 //============================================================================
163 Word wraps a string. The wordWidth function is guaranteed to be called exactly
164 once for each word in the string, so it may be stateful, no idea what that
165 would be good for any more. At the beginning of the string, it will be called
166 for the char 0 to initialize a clean state, and then once with the string " "
167 (a space) so the routine knows how long a space is.
169 In case no single character fits into the given width, the wordWidth function
170 must return the width of exactly one character.
172 Wrapped lines get the isContinuation flag set and are continuationWidth less wide.
174 The sum of the return values of the processLine function will be returned.
177 int COM_Wordwrap(const char *string, size_t length, float continuationWidth, float maxWidth, COM_WordWidthFunc_t wordWidth, void *passthroughCW, COM_LineProcessorFunc processLine, void *passthroughPL)
179 // Logic is as follows:
181 // For each word or whitespace:
182 // Newline found? Output current line, advance to next line. This is not a continuation. Continue.
183 // Space found? Always add it to the current line, no matter if it fits.
184 // Word found? Check if current line + current word fits.
185 // If it fits, append it. Continue.
186 // If it doesn't fit, output current line, advance to next line. Append the word. This is a continuation. Continue.
188 qboolean isContinuation = false;
190 const char *startOfLine = string;
191 const char *cursor = string;
192 const char *end = string + length;
193 float spaceUsedInLine = 0;
194 float spaceUsedForWord;
200 wordWidth(passthroughCW, NULL, &dummy, -1);
202 spaceWidth = wordWidth(passthroughCW, " ", &dummy, -1);
206 char ch = (cursor < end) ? *cursor : 0;
209 case 0: // end of string
210 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
212 case '\n': // end of line
213 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
214 isContinuation = false;
216 startOfLine = cursor;
220 spaceUsedInLine += spaceWidth;
224 while(cursor + wordLen < end)
226 switch(cursor[wordLen])
238 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
239 if(wordLen < 1) // cannot happen according to current spec of wordWidth
242 spaceUsedForWord = maxWidth + 1; // too high, forces it in a line of itself
244 if(spaceUsedInLine + spaceUsedForWord <= maxWidth || cursor == startOfLine)
246 // we can simply append it
248 spaceUsedInLine += spaceUsedForWord;
252 // output current line
253 result += processLine(passthroughPL, startOfLine, cursor - startOfLine, spaceUsedInLine, isContinuation);
254 isContinuation = true;
255 startOfLine = cursor;
257 spaceUsedInLine = continuationWidth + spaceUsedForWord;
266 qboolean isContinuation = false;
267 float currentWordSpace = 0;
268 const char *currentWord = 0;
269 float minReserve = 0;
271 float spaceUsedInLine = 0;
272 const char *currentLine = 0;
273 const char *currentLineEnd = 0;
274 float currentLineFinalWhitespace = 0;
278 minReserve = charWidth(passthroughCW, 0);
279 minReserve += charWidth(passthroughCW, ' ');
281 if(maxWidth < continuationWidth + minReserve)
282 maxWidth = continuationWidth + minReserve;
284 charWidth(passthroughCW, 0);
286 for(p = string; p < string + length; ++p)
289 float w = charWidth(passthroughCW, c);
294 currentWordSpace = 0;
300 spaceUsedInLine = isContinuation ? continuationWidth : 0;
306 // 1. I can add the word AND a space - then just append it.
307 if(spaceUsedInLine + currentWordSpace + w <= maxWidth)
309 currentLineEnd = p; // note: space not included here
310 currentLineFinalWhitespace = w;
311 spaceUsedInLine += currentWordSpace + w;
313 // 2. I can just add the word - then append it, output current line and go to next one.
314 else if(spaceUsedInLine + currentWordSpace <= maxWidth)
316 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
318 isContinuation = true;
320 // 3. Otherwise, output current line and go to next one, where I can add the word.
321 else if(continuationWidth + currentWordSpace + w <= maxWidth)
324 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
325 currentLine = currentWord;
326 spaceUsedInLine = continuationWidth + currentWordSpace + w;
328 currentLineFinalWhitespace = w;
329 isContinuation = true;
331 // 4. We can't even do that? Then output both current and next word as new lines.
336 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
337 isContinuation = true;
339 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
341 isContinuation = true;
347 // 1. I can add the word - then do it.
348 if(spaceUsedInLine + currentWordSpace <= maxWidth)
350 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
352 // 2. Otherwise, output current line, next one and make tabula rasa.
357 processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
358 isContinuation = true;
360 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
364 isContinuation = false;
368 currentWordSpace += w;
370 spaceUsedInLine + currentWordSpace > maxWidth // can't join this line...
372 continuationWidth + currentWordSpace > maxWidth // can't join any other line...
375 // this word cannot join ANY line...
376 // so output the current line...
379 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
380 isContinuation = true;
383 // then this word's beginning...
386 // it may not fit, but we know we have to split it into maxWidth - continuationWidth pieces
387 float pieceWidth = maxWidth - continuationWidth;
388 const char *pos = currentWord;
389 currentWordSpace = 0;
391 // reset the char width function to a state where no kerning occurs (start of word)
392 charWidth(passthroughCW, ' ');
395 float w = charWidth(passthroughCW, *pos);
396 if(currentWordSpace + w > pieceWidth) // this piece won't fit any more
398 // print everything until it
399 result += processLine(passthroughPL, currentWord, pos - currentWord, currentWordSpace, true);
402 currentWordSpace = 0;
404 currentWordSpace += w;
407 // now we have a currentWord that fits... set up its next line
408 // currentWordSpace has been set
409 // currentWord has been set
410 spaceUsedInLine = continuationWidth;
411 currentLine = currentWord;
413 isContinuation = true;
417 // we have a guarantee that it will fix (see if clause)
418 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace - w, isContinuation);
420 // and use the rest of this word as new start of a line
421 currentWordSpace = w;
423 spaceUsedInLine = continuationWidth;
426 isContinuation = true;
435 currentWordSpace = 0;
438 if(currentLine) // Same procedure as \n
440 // Can I append the current word?
441 if(spaceUsedInLine + currentWordSpace <= maxWidth)
442 result += processLine(passthroughPL, currentLine, p - currentLine, spaceUsedInLine + currentWordSpace, isContinuation);
447 result += processLine(passthroughPL, currentLine, currentLineEnd - currentLine, spaceUsedInLine - currentLineFinalWhitespace, isContinuation);
448 isContinuation = true;
450 result += processLine(passthroughPL, currentWord, p - currentWord, currentWordSpace, isContinuation);
460 COM_ParseToken_Simple
462 Parse a token out of a string
465 int COM_ParseToken_Simple(const char **datapointer, qboolean returnnewline, qboolean parsebackslash, qboolean parsecomments)
469 const char *data = *datapointer;
486 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
496 // handle Windows line ending
497 if (data[0] == '\r' && data[1] == '\n')
500 if (parsecomments && data[0] == '/' && data[1] == '/')
503 while (*data && *data != '\n' && *data != '\r')
507 else if (parsecomments && data[0] == '/' && data[1] == '*')
511 while (*data && (data[0] != '*' || data[1] != '/'))
519 else if (*data == '\"')
522 for (data++;*data && *data != '\"';data++)
525 if (*data == '\\' && parsebackslash)
534 if (len < (int)sizeof(com_token) - 1)
535 com_token[len++] = c;
543 else if (*data == '\r')
545 // translate Mac line ending to UNIX
546 com_token[len++] = '\n';data++;
551 else if (*data == '\n')
554 com_token[len++] = *data++;
562 for (;!ISWHITESPACE(*data);data++)
563 if (len < (int)sizeof(com_token) - 1)
564 com_token[len++] = *data;
573 COM_ParseToken_QuakeC
575 Parse a token out of a string
578 int COM_ParseToken_QuakeC(const char **datapointer, qboolean returnnewline)
582 const char *data = *datapointer;
599 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
609 // handle Windows line ending
610 if (data[0] == '\r' && data[1] == '\n')
613 if (data[0] == '/' && data[1] == '/')
616 while (*data && *data != '\n' && *data != '\r')
620 else if (data[0] == '/' && data[1] == '*')
624 while (*data && (data[0] != '*' || data[1] != '/'))
632 else if (*data == '\"' || *data == '\'')
636 for (data++;*data && *data != quote;data++)
648 if (len < (int)sizeof(com_token) - 1)
649 com_token[len++] = c;
657 else if (*data == '\r')
659 // translate Mac line ending to UNIX
660 com_token[len++] = '\n';data++;
665 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
668 com_token[len++] = *data++;
676 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
677 if (len < (int)sizeof(com_token) - 1)
678 com_token[len++] = *data;
687 COM_ParseToken_VM_Tokenize
689 Parse a token out of a string
692 int COM_ParseToken_VM_Tokenize(const char **datapointer, qboolean returnnewline)
696 const char *data = *datapointer;
713 for (;ISWHITESPACE(*data) && ((*data != '\n' && *data != '\r') || !returnnewline);data++)
723 // handle Windows line ending
724 if (data[0] == '\r' && data[1] == '\n')
727 if (data[0] == '/' && data[1] == '/')
730 while (*data && *data != '\n' && *data != '\r')
734 else if (data[0] == '/' && data[1] == '*')
738 while (*data && (data[0] != '*' || data[1] != '/'))
746 else if (*data == '\"' || *data == '\'')
750 for (data++;*data && *data != quote;data++)
762 if (len < (int)sizeof(com_token) - 1)
763 com_token[len++] = c;
771 else if (*data == '\r')
773 // translate Mac line ending to UNIX
774 com_token[len++] = '\n';data++;
779 else if (*data == '\n' || *data == '{' || *data == '}' || *data == ')' || *data == '(' || *data == ']' || *data == '[' || *data == ':' || *data == ',' || *data == ';')
782 com_token[len++] = *data++;
790 for (;!ISWHITESPACE(*data) && *data != '{' && *data != '}' && *data != ')' && *data != '(' && *data != ']' && *data != '[' && *data != ':' && *data != ',' && *data != ';';data++)
791 if (len < (int)sizeof(com_token) - 1)
792 com_token[len++] = *data;
801 COM_ParseToken_Console
803 Parse a token out of a string, behaving like the qwcl console
806 int COM_ParseToken_Console(const char **datapointer)
809 const char *data = *datapointer;
822 for (;ISWHITESPACE(*data);data++)
832 if (*data == '/' && data[1] == '/')
835 while (*data && *data != '\n' && *data != '\r')
839 else if (*data == '\"')
842 for (data++;*data && *data != '\"';data++)
844 // allow escaped " and \ case
845 if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
847 if (len < (int)sizeof(com_token) - 1)
848 com_token[len++] = *data;
858 for (;!ISWHITESPACE(*data);data++)
859 if (len < (int)sizeof(com_token) - 1)
860 com_token[len++] = *data;
873 Returns the position (1 to argc-1) in the program's argument list
874 where the given parameter apears, or 0 if not present
877 int COM_CheckParm (const char *parm)
881 for (i=1 ; i<sys.argc ; i++)
884 continue; // NEXTSTEP sometimes clears appkit vars.
885 if (!strcmp (parm,sys.argv[i]))
892 //===========================================================================
896 gamemode_t com_startupgamemode;
897 gamemode_t com_startupgamegroup;
899 typedef struct gamemode_info_s
901 gamemode_t mode; // this gamemode
902 gamemode_t group; // different games with same group can switch automatically when gamedirs change
903 const char* prog_name; // not null
904 const char* cmdline; // not null
905 const char* gamename; // not null
906 const char* gamenetworkfiltername; // not null
907 const char* gamedirname1; // not null
908 const char* gamedirname2; // null
909 const char* gamescreenshotname; // not nul
910 const char* gameuserdirname; // not null
913 static const gamemode_info_t gamemode_info [GAME_COUNT] =
914 {// game basegame prog_name cmdline gamename gamenetworkfilername basegame modgame screenshot userdir // commandline option
915 { GAME_NORMAL, GAME_NORMAL, "", "-quake", "DarkPlaces-Quake", "DarkPlaces-Quake", "id1", NULL, "dp", "darkplaces" }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
916 { 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
917 { 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
918 { 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
919 { 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
920 { GAME_NEXUIZ, GAME_NEXUIZ, "nexuiz", "-nexuiz", "Nexuiz", "Nexuiz", "data", NULL, "nexuiz", "nexuiz" }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
921 { GAME_XONOTIC, GAME_XONOTIC, "xonotic", "-xonotic", "Xonotic", "Xonotic", "data", NULL, "xonotic", "xonotic" }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
922 { GAME_TRANSFUSION, GAME_TRANSFUSION, "transfusion", "-transfusion", "Transfusion", "Transfusion", "basetf", NULL, "transfusion", "transfusion" }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
923 { 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
924 { 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)
925 { GAME_BATTLEMECH, GAME_BATTLEMECH, "battlemech", "-battlemech", "Battlemech", "Battlemech", "base", NULL, "battlemech", "battlemech" }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
926 { GAME_ZYMOTIC, GAME_ZYMOTIC, "zymotic", "-zymotic", "Zymotic", "Zymotic", "basezym", NULL, "zymotic", "zymotic" }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
927 { GAME_SETHERAL, GAME_SETHERAL, "setheral", "-setheral", "Setheral", "Setheral", "data", NULL, "setheral", "setheral" }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
928 { 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)
929 { GAME_NEOTERIC, GAME_NORMAL, "neoteric", "-neoteric", "Neoteric", "Neoteric", "id1", "neobase", "neo", "darkplaces" }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
930 { 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
931 { 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
932 { GAME_DELUXEQUAKE, GAME_DELUXEQUAKE, "dq", "-dq", "Deluxe Quake", "Deluxe_Quake", "basedq", "extradq", "basedq", "dq" }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
933 { GAME_THEHUNTED, GAME_THEHUNTED, "thehunted", "-thehunted", "The Hunted", "The_Hunted", "thdata", NULL, "th", "thehunted" }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
934 { 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
935 { GAME_DARSANA, GAME_DARSANA, "darsana", "-darsana", "Darsana", "Darsana", "ddata", NULL, "darsana", "darsana" }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
936 { GAME_CONTAGIONTHEORY, GAME_CONTAGIONTHEORY, "contagiontheory", "-contagiontheory", "Contagion Theory", "Contagion_Theory", "ctdata", NULL, "ct", "contagiontheory" }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
937 { GAME_EDU2P, GAME_EDU2P, "edu2p", "-edu2p", "EDU2 Prototype", "EDU2_Prototype", "id1", "edu2", "edu2_p", "edu2prototype" }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
938 { GAME_PROPHECY, GAME_PROPHECY, "prophecy", "-prophecy", "Prophecy", "Prophecy", "gamedata", NULL, "phcy", "prophecy" }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
939 { GAME_BLOODOMNICIDE, GAME_BLOODOMNICIDE, "omnicide", "-omnicide", "Blood Omnicide", "Blood_Omnicide", "kain", NULL, "omnicide", "omnicide" }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
940 { GAME_STEELSTORM, GAME_STEELSTORM, "steelstorm", "-steelstorm", "Steel-Storm", "Steel-Storm", "gamedata", NULL, "ss", "steelstorm" }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
941 { 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
942 { 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.
943 { 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
944 { 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
945 { 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
946 { GAME_MOONHELM, GAME_MOONHELM, "moonhelm", "-moonhelm", "MoonHelm", "MoonHelm", "data", NULL, "mh", "moonhelm" }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
947 { GAME_VORETOURNAMENT, GAME_VORETOURNAMENT, "voretournament", "-voretournament", "Vore Tournament", "Vore_Tournament", "data", NULL, "voretournament", "voretournament" }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
950 static void COM_SetGameType(int index);
951 void COM_InitGameType (void)
953 char name [MAX_OSPATH];
958 COM_ToLowerString(FORCEGAME, name, sizeof (name));
960 // check executable filename for keywords, but do it SMARTLY - only check the last path element
961 FS_StripExtension(FS_FileWithoutPath(sys.argv[0]), name, sizeof (name));
962 COM_ToLowerString(name, name, sizeof (name));
964 for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
965 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
968 // check commandline options for keywords
969 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
970 if (COM_CheckParm (gamemode_info[i].cmdline))
973 com_startupgamemode = gamemode_info[index].mode;
974 com_startupgamegroup = gamemode_info[index].group;
975 COM_SetGameType(index);
978 void COM_ChangeGameTypeForGameDirs(void)
982 // this will not not change the gamegroup
983 // first check if a base game (single gamedir) matches
984 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
986 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
992 // now that we have a base game, see if there is a matching derivative game (two gamedirs)
995 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
997 if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
1004 // we now have a good guess at which game this is meant to be...
1005 if (index >= 0 && gamemode != gamemode_info[index].mode)
1006 COM_SetGameType(index);
1009 static void COM_SetGameType(int index)
1011 static char gamenetworkfilternamebuffer[64];
1013 if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
1015 gamemode = gamemode_info[index].mode;
1016 gamename = gamemode_info[index].gamename;
1017 gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
1018 gamedirname1 = gamemode_info[index].gamedirname1;
1019 gamedirname2 = gamemode_info[index].gamedirname2;
1020 gamescreenshotname = gamemode_info[index].gamescreenshotname;
1021 gameuserdirname = gamemode_info[index].gameuserdirname;
1023 if (gamemode == com_startupgamemode)
1025 if((t = COM_CheckParm("-customgamename")) && t + 1 < sys.argc)
1026 gamename = gamenetworkfiltername = sys.argv[t+1];
1027 if((t = COM_CheckParm("-customgamenetworkfiltername")) && t + 1 < sys.argc)
1028 gamenetworkfiltername = sys.argv[t+1];
1029 if((t = COM_CheckParm("-customgamedirname1")) && t + 1 < sys.argc)
1030 gamedirname1 = sys.argv[t+1];
1031 if((t = COM_CheckParm("-customgamedirname2")) && t + 1 < sys.argc)
1032 gamedirname2 = *sys.argv[t+1] ? sys.argv[t+1] : NULL;
1033 if((t = COM_CheckParm("-customgamescreenshotname")) && t + 1 < sys.argc)
1034 gamescreenshotname = sys.argv[t+1];
1035 if((t = COM_CheckParm("-customgameuserdirname")) && t + 1 < sys.argc)
1036 gameuserdirname = sys.argv[t+1];
1039 if (gamedirname2 && gamedirname2[0])
1040 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
1042 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
1043 for (i = 0;i < fs_numgamedirs;i++)
1046 Con_Printf(", with mod gamedirs");
1047 Con_Printf(" %s", fs_gamedirs[i]);
1051 if (strchr(gamenetworkfiltername, ' '))
1054 // if there are spaces in the game's network filter name it would
1055 // cause parse errors in getservers in dpmaster, so we need to replace
1056 // them with _ characters
1057 strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
1058 while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
1060 gamenetworkfiltername = gamenetworkfilternamebuffer;
1063 Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
1072 void COM_Init_Commands (void)
1075 char com_cmdline[MAX_INPUTLINE];
1077 Cvar_RegisterVariable (®istered);
1078 Cvar_RegisterVariable (&cmdline);
1080 // reconstitute the command line for the cmdline externally visible cvar
1082 for (j = 0;(j < MAX_NUM_ARGVS) && (j < sys.argc);j++)
1085 if (strstr(sys.argv[j], " "))
1087 // arg contains whitespace, store quotes around it
1088 // This condition checks whether we can allow to put
1089 // in two quote characters.
1090 if (n >= ((int)sizeof(com_cmdline) - 2))
1092 com_cmdline[n++] = '\"';
1093 // This condition checks whether we can allow one
1094 // more character and a quote character.
1095 while ((n < ((int)sizeof(com_cmdline) - 2)) && sys.argv[j][i])
1096 // FIXME: Doesn't quote special characters.
1097 com_cmdline[n++] = sys.argv[j][i++];
1098 com_cmdline[n++] = '\"';
1102 // This condition checks whether we can allow one
1104 while ((n < ((int)sizeof(com_cmdline) - 1)) && sys.argv[j][i])
1105 com_cmdline[n++] = sys.argv[j][i++];
1107 if (n < ((int)sizeof(com_cmdline) - 1))
1108 com_cmdline[n++] = ' ';
1113 Cvar_SetQuick(&cmdline, com_cmdline);
1120 varargs print into provided buffer, returns buffer (so that it can be called in-line, unlike dpsnprintf)
1123 char *va(char *buf, size_t buflen, const char *format, ...)
1127 va_start (argptr, format);
1128 dpvsnprintf (buf, buflen, format,argptr);
1135 //======================================
1137 // snprintf and vsnprintf are NOT portable. Use their DP counterparts instead
1143 # define snprintf _snprintf
1144 # define vsnprintf _vsnprintf
1148 int dpsnprintf (char *buffer, size_t buffersize, const char *format, ...)
1153 va_start (args, format);
1154 result = dpvsnprintf (buffer, buffersize, format, args);
1161 int dpvsnprintf (char *buffer, size_t buffersize, const char *format, va_list args)
1165 #if _MSC_VER >= 1400
1166 result = _vsnprintf_s (buffer, buffersize, _TRUNCATE, format, args);
1168 result = vsnprintf (buffer, buffersize, format, args);
1170 if (result < 0 || (size_t)result >= buffersize)
1172 buffer[buffersize - 1] = '\0';
1180 //======================================
1182 void COM_ToLowerString (const char *in, char *out, size_t size_out)
1187 if(utf8_enable.integer)
1190 while(*in && size_out > 1)
1193 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1194 ch = u8_tolower(ch);
1195 n = u8_fromchar(ch, out, size_out);
1204 while (*in && size_out > 1)
1206 if (*in >= 'A' && *in <= 'Z')
1207 *out++ = *in++ + 'a' - 'A';
1215 void COM_ToUpperString (const char *in, char *out, size_t size_out)
1220 if(utf8_enable.integer)
1223 while(*in && size_out > 1)
1226 Uchar ch = u8_getchar_utf8_enabled(in, &in);
1227 ch = u8_toupper(ch);
1228 n = u8_fromchar(ch, out, size_out);
1237 while (*in && size_out > 1)
1239 if (*in >= 'a' && *in <= 'z')
1240 *out++ = *in++ + 'A' - 'a';
1248 int COM_StringBeginsWith(const char *s, const char *match)
1250 for (;*s && *match;s++, match++)
1256 int COM_ReadAndTokenizeLine(const char **text, char **argv, int maxargc, char *tokenbuf, int tokenbufsize, const char *commentprefix)
1258 int argc, commentprefixlength;
1262 tokenbufend = tokenbuf + tokenbufsize;
1264 commentprefixlength = 0;
1266 commentprefixlength = (int)strlen(commentprefix);
1267 while (*l && *l != '\n' && *l != '\r')
1269 if (!ISWHITESPACE(*l))
1271 if (commentprefixlength && !strncmp(l, commentprefix, commentprefixlength))
1273 while (*l && *l != '\n' && *l != '\r')
1277 if (argc >= maxargc)
1279 argv[argc++] = tokenbuf;
1283 while (*l && *l != '"')
1285 if (tokenbuf >= tokenbufend)
1294 while (!ISWHITESPACE(*l))
1296 if (tokenbuf >= tokenbufend)
1301 if (tokenbuf >= tokenbufend)
1322 COM_StringLengthNoColors
1324 calculates the visible width of a color coded string.
1326 *valid is filled with TRUE if the string is a valid colored string (that is, if
1327 it does not end with an unfinished color code). If it gets filled with FALSE, a
1328 fix would be adding a STRING_COLOR_TAG at the end of the string.
1330 valid can be set to NULL if the caller doesn't care.
1332 For size_s, specify the maximum number of characters from s to use, or 0 to use
1333 all characters until the zero terminator.
1337 COM_StringLengthNoColors(const char *s, size_t size_s, qboolean *valid)
1339 const char *end = size_s ? (s + size_s) : NULL;
1343 switch((s == end) ? 0 : *s)
1349 case STRING_COLOR_TAG:
1351 switch((s == end) ? 0 : *s)
1353 case STRING_COLOR_RGB_TAG_CHAR:
1354 if (s+1 != end && isxdigit(s[1]) &&
1355 s+2 != end && isxdigit(s[2]) &&
1356 s+3 != end && isxdigit(s[3]) )
1361 ++len; // STRING_COLOR_TAG
1362 ++len; // STRING_COLOR_RGB_TAG_CHAR
1364 case 0: // ends with unfinished color code!
1369 case STRING_COLOR_TAG: // escaped ^
1372 case '0': case '1': case '2': case '3': case '4':
1373 case '5': case '6': case '7': case '8': case '9': // color code
1375 default: // not a color code
1376 ++len; // STRING_COLOR_TAG
1377 ++len; // the character
1392 COM_StringDecolorize
1394 removes color codes from a string.
1396 If escape_carets is true, the resulting string will be safe for printing. If
1397 escape_carets is false, the function will just strip color codes (for logging
1400 If the output buffer size did not suffice for converting, the function returns
1401 FALSE. Generally, if escape_carets is false, the output buffer needs
1402 strlen(str)+1 bytes, and if escape_carets is true, it can need strlen(str)*1.5+2
1403 bytes. In any case, the function makes sure that the resulting string is
1406 For size_in, specify the maximum number of characters from in to use, or 0 to use
1407 all characters until the zero terminator.
1411 COM_StringDecolorize(const char *in, size_t size_in, char *out, size_t size_out, qboolean escape_carets)
1413 #define APPEND(ch) do { if(--size_out) { *out++ = (ch); } else { *out++ = 0; return false; } } while(0)
1414 const char *end = size_in ? (in + size_in) : NULL;
1419 switch((in == end) ? 0 : *in)
1424 case STRING_COLOR_TAG:
1426 switch((in == end) ? 0 : *in)
1428 case STRING_COLOR_RGB_TAG_CHAR:
1429 if (in+1 != end && isxdigit(in[1]) &&
1430 in+2 != end && isxdigit(in[2]) &&
1431 in+3 != end && isxdigit(in[3]) )
1436 APPEND(STRING_COLOR_TAG);
1438 APPEND(STRING_COLOR_TAG);
1439 APPEND(STRING_COLOR_RGB_TAG_CHAR);
1441 case 0: // ends with unfinished color code!
1442 APPEND(STRING_COLOR_TAG);
1443 // finish the code by appending another caret when escaping
1445 APPEND(STRING_COLOR_TAG);
1448 case STRING_COLOR_TAG: // escaped ^
1449 APPEND(STRING_COLOR_TAG);
1450 // append a ^ twice when escaping
1452 APPEND(STRING_COLOR_TAG);
1454 case '0': case '1': case '2': case '3': case '4':
1455 case '5': case '6': case '7': case '8': case '9': // color code
1457 default: // not a color code
1458 APPEND(STRING_COLOR_TAG);
1473 char *InfoString_GetValue(const char *buffer, const char *key, char *value, size_t valuelength)
1479 keylength = strlen(key);
1480 if (valuelength < 1 || !value)
1482 Con_Printf("InfoString_GetValue: no room in value\n");
1486 if (strchr(key, '\\'))
1488 Con_Printf("InfoString_GetValue: key name \"%s\" contains \\ which is not possible in an infostring\n", key);
1491 if (strchr(key, '\"'))
1493 Con_Printf("InfoString_SetValue: key name \"%s\" contains \" which is not allowed in an infostring\n", key);
1498 Con_Printf("InfoString_GetValue: can not look up a key with no name\n");
1501 while (buffer[pos] == '\\')
1503 if (!memcmp(buffer + pos+1, key, keylength) &&
1504 (buffer[pos+1 + keylength] == 0 ||
1505 buffer[pos+1 + keylength] == '\\'))
1507 pos += 1 + (int)keylength; // Skip \key
1508 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1509 for (j = 0;buffer[pos+j] && buffer[pos+j] != '\\' && j < (int)valuelength - 1;j++)
1510 value[j] = buffer[pos+j];
1514 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1515 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1516 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1517 for (pos++;buffer[pos] && buffer[pos] != '\\';pos++);
1519 // if we reach this point the key was not found
1523 void InfoString_SetValue(char *buffer, size_t bufferlength, const char *key, const char *value)
1531 keylength = strlen(key);
1532 if (strchr(key, '\\') || strchr(value, '\\'))
1534 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \\ which is not possible to store in an infostring\n", key, value);
1537 if (strchr(key, '\"') || strchr(value, '\"'))
1539 Con_Printf("InfoString_SetValue: \"%s\" \"%s\" contains \" which is not allowed in an infostring\n", key, value);
1544 Con_Printf("InfoString_SetValue: can not set a key with no name\n");
1547 while (buffer[pos] == '\\')
1549 if (!memcmp(buffer + pos+1, key, keylength) &&
1550 (buffer[pos+1 + keylength] == 0 ||
1551 buffer[pos+1 + keylength] == '\\'))
1553 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1554 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1555 if (buffer[pos] == '\\') pos++; // Skip \ before value.
1556 for (;buffer[pos] && buffer[pos] != '\\';pos++);
1558 // if we found the key, find the end of it because we will be replacing it
1560 if (buffer[pos] == '\\')
1562 pos2 += 1 + (int)keylength; // Skip \key
1563 if (buffer[pos2] == '\\') pos2++; // Skip \ before value.
1564 for (;buffer[pos2] && buffer[pos2] != '\\';pos2++);
1566 if (bufferlength <= pos + 1 + strlen(key) + 1 + strlen(value) + strlen(buffer + pos2))
1568 Con_Printf("InfoString_SetValue: no room for \"%s\" \"%s\" in infostring\n", key, value);
1573 // set the key/value and append the remaining text
1574 char tempbuffer[MAX_INPUTLINE];
1575 strlcpy(tempbuffer, buffer + pos2, sizeof(tempbuffer));
1576 dpsnprintf(buffer + pos, bufferlength - pos, "\\%s\\%s%s", key, value, tempbuffer);
1580 // just remove the key from the text
1581 strlcpy(buffer + pos, buffer + pos2, bufferlength - pos);
1585 void InfoString_Print(char *buffer)
1588 char key[MAX_INPUTLINE];
1589 char value[MAX_INPUTLINE];
1592 if (*buffer != '\\')
1594 Con_Printf("InfoString_Print: corrupt string\n");
1597 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1598 if (i < (int)sizeof(key)-1)
1601 if (*buffer != '\\')
1603 Con_Printf("InfoString_Print: corrupt string\n");
1606 for (buffer++, i = 0;*buffer && *buffer != '\\';buffer++)
1607 if (i < (int)sizeof(value)-1)
1608 value[i++] = *buffer;
1610 // empty value is an error case
1611 Con_Printf("%20s %s\n", key, value[0] ? value : "NO VALUE");
1615 //========================================================
1616 // strlcat and strlcpy, from OpenBSD
1619 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
1621 * Permission to use, copy, modify, and distribute this software for any
1622 * purpose with or without fee is hereby granted, provided that the above
1623 * copyright notice and this permission notice appear in all copies.
1625 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1626 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1627 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1628 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1629 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1630 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1631 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1634 /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
1635 /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
1638 #ifndef HAVE_STRLCAT
1640 strlcat(char *dst, const char *src, size_t siz)
1642 register char *d = dst;
1643 register const char *s = src;
1644 register size_t n = siz;
1647 /* Find the end of dst and adjust bytes left but don't go past end */
1648 while (n-- != 0 && *d != '\0')
1654 return(dlen + strlen(s));
1655 while (*s != '\0') {
1664 return(dlen + (s - src)); /* count does not include NUL */
1666 #endif // #ifndef HAVE_STRLCAT
1669 #ifndef HAVE_STRLCPY
1671 strlcpy(char *dst, const char *src, size_t siz)
1673 register char *d = dst;
1674 register const char *s = src;
1675 register size_t n = siz;
1677 /* Copy as many bytes as will fit */
1678 if (n != 0 && --n != 0) {
1680 if ((*d++ = *s++) == 0)
1685 /* Not enough room in dst, add NUL and traverse rest of src */
1688 *d = '\0'; /* NUL-terminate dst */
1693 return(s - src - 1); /* count does not include NUL */
1696 #endif // #ifndef HAVE_STRLCPY
1698 void FindFraction(double val, int *num, int *denom, int denomMax)
1703 bestdiff = fabs(val);
1707 for(i = 1; i <= denomMax; ++i)
1709 int inum = (int) floor(0.5 + val * i);
1710 double diff = fabs(val - inum / (double)i);
1720 // decodes an XPM from C syntax
1721 char **XPM_DecodeString(const char *in)
1723 static char *tokens[257];
1724 static char lines[257][512];
1727 // skip until "{" token
1728 while(COM_ParseToken_QuakeC(&in, false) && strcmp(com_token, "{"));
1730 // now, read in succession: string, comma-or-}
1731 while(COM_ParseToken_QuakeC(&in, false))
1733 tokens[line] = lines[line];
1734 strlcpy(lines[line++], com_token, sizeof(lines[0]));
1735 if(!COM_ParseToken_QuakeC(&in, false))
1737 if(!strcmp(com_token, "}"))
1739 if(strcmp(com_token, ","))
1741 if(line >= sizeof(tokens) / sizeof(tokens[0]))
1748 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1749 static void base64_3to4(const unsigned char *in, unsigned char *out, int bytes)
1751 unsigned char i0 = (bytes > 0) ? in[0] : 0;
1752 unsigned char i1 = (bytes > 1) ? in[1] : 0;
1753 unsigned char i2 = (bytes > 2) ? in[2] : 0;
1754 unsigned char o0 = base64[i0 >> 2];
1755 unsigned char o1 = base64[((i0 << 4) | (i1 >> 4)) & 077];
1756 unsigned char o2 = base64[((i1 << 2) | (i2 >> 6)) & 077];
1757 unsigned char o3 = base64[i2 & 077];
1758 out[0] = (bytes > 0) ? o0 : '?';
1759 out[1] = (bytes > 0) ? o1 : '?';
1760 out[2] = (bytes > 1) ? o2 : '=';
1761 out[3] = (bytes > 2) ? o3 : '=';
1764 size_t base64_encode(unsigned char *buf, size_t buflen, size_t outbuflen)
1767 // expand the out-buffer
1768 blocks = (buflen + 2) / 3;
1769 if(blocks*4 > outbuflen)
1771 for(i = blocks; i > 0; )
1774 base64_3to4(buf + 3*i, buf + 4*i, (int)(buflen - 3*i));