]> git.xonotic.org Git - xonotic/darkplaces.git/blob - com_game.c
image: Check if file exists first to allow early out and avoid a slower codepath
[xonotic/darkplaces.git] / com_game.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2020 Ashley Rose "LadyHavoc" Hale
4 Copyright (C) 2020 David "Cloudwalk" Knapp
5 Copyright (C) 2020 Kristus <kristustf@gmail.com>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22 */
23
24 #include "quakedef.h"
25
26 // Game mods
27
28 gamemode_t gamemode;
29 const char *gamename;
30 const char *gamenetworkfiltername; // same as gamename currently but with _ in place of spaces so that "getservers" packets parse correctly (this also means the 
31 const char *gamedirname1;
32 const char *gamedirname2;
33 const char *gamescreenshotname;
34 const char *gameuserdirname;
35 char com_modname[MAX_OSPATH] = "";
36
37 gamemode_t com_startupgamemode;
38 gamemode_t com_startupgamegroup;
39
40 typedef struct gamemode_info_s
41 {
42         gamemode_t mode; // this gamemode
43         gamemode_t group; // different games with same group can switch automatically when gamedirs change
44         const char* prog_name; // not null
45         const char* cmdline; // not null
46         const char* gamename; // not null
47         const char*     gamenetworkfiltername; // not null
48         const char* gamedirname1; // not null
49         const char* gamedirname2; // null
50         const char* gamescreenshotname; // not nul
51         const char* gameuserdirname; // not null
52 } gamemode_info_t;
53
54 static const gamemode_info_t gamemode_info [GAME_COUNT] =
55 {// game                                                basegame                                        prog_name                               cmdline                                         gamename                                        gamenetworkfilername            basegame        modgame                 screenshot                      userdir                                    // commandline option
56 { GAME_NORMAL,                                  GAME_NORMAL,                            "",                                             "-quake",                                       "DarkPlaces-Quake",                     "DarkPlaces-Quake",                     "id1",          NULL,                   "dp",                           "darkplaces"                    }, // COMMANDLINEOPTION: Game: -quake runs the game Quake (default)
57 { 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
58 { 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
59 { 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
60 { 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
61 { GAME_NEXUIZ,                                  GAME_NEXUIZ,                            "nexuiz",                               "-nexuiz",                                      "Nexuiz",                                       "Nexuiz",                                       "data",         NULL,                   "nexuiz",                       "nexuiz"                                }, // COMMANDLINEOPTION: Game: -nexuiz runs the multiplayer game Nexuiz
62 { GAME_XONOTIC,                                 GAME_XONOTIC,                           "xonotic",                              "-xonotic",                                     "Xonotic",                                      "Xonotic",                                      "data",         NULL,                   "xonotic",                      "xonotic"                               }, // COMMANDLINEOPTION: Game: -xonotic runs the multiplayer game Xonotic
63 { GAME_TRANSFUSION,                             GAME_TRANSFUSION,                       "transfusion",                  "-transfusion",                         "Transfusion",                          "Transfusion",                          "basetf",       NULL,                   "transfusion",          "transfusion"                   }, // COMMANDLINEOPTION: Game: -transfusion runs Transfusion (the recreation of Blood in Quake)
64 { 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
65 { 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)
66 { GAME_BATTLEMECH,                              GAME_BATTLEMECH,                        "battlemech",                   "-battlemech",                          "Battlemech",                           "Battlemech",                           "base",         NULL,                   "battlemech",           "battlemech"                    }, // COMMANDLINEOPTION: Game: -battlemech runs the multiplayer topdown deathmatch game BattleMech
67 { GAME_ZYMOTIC,                                 GAME_ZYMOTIC,                           "zymotic",                              "-zymotic",                                     "Zymotic",                                      "Zymotic",                                      "basezym",      NULL,                   "zymotic",                      "zymotic"                               }, // COMMANDLINEOPTION: Game: -zymotic runs the singleplayer game Zymotic
68 { GAME_SETHERAL,                                GAME_SETHERAL,                          "setheral",                             "-setheral",                            "Setheral",                                     "Setheral",                                     "data",         NULL,                   "setheral",                     "setheral"                              }, // COMMANDLINEOPTION: Game: -setheral runs the multiplayer game Setheral
69 { 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)
70 { GAME_NEOTERIC,                                GAME_NORMAL,                            "neoteric",                             "-neoteric",                            "Neoteric",                                     "Neoteric",                                     "id1",          "neobase",              "neo",                          "darkplaces"                    }, // COMMANDLINEOPTION: Game: -neoteric runs the game Neoteric
71 { 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
72 { 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
73 { GAME_DELUXEQUAKE,                             GAME_DELUXEQUAKE,                       "dq",                                   "-dq",                                          "Deluxe Quake",                         "Deluxe_Quake",                         "basedq",       "extradq",              "basedq",                       "dq"                                    }, // COMMANDLINEOPTION: Game: -dq runs the game Deluxe Quake
74 { GAME_THEHUNTED,                               GAME_THEHUNTED,                         "thehunted",                    "-thehunted",                           "The Hunted",                           "The_Hunted",                           "thdata",       NULL,                   "th",                           "thehunted"                             }, // COMMANDLINEOPTION: Game: -thehunted runs the game The Hunted
75 { 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
76 { GAME_DARSANA,                                 GAME_DARSANA,                           "darsana",                              "-darsana",                                     "Darsana",                                      "Darsana",                                      "ddata",        NULL,                   "darsana",                      "darsana"                               }, // COMMANDLINEOPTION: Game: -darsana runs the game Darsana
77 { GAME_CONTAGIONTHEORY,                 GAME_CONTAGIONTHEORY,           "contagiontheory",              "-contagiontheory",                     "Contagion Theory",                     "Contagion_Theory",                     "ctdata",       NULL,                   "ct",                           "contagiontheory"               }, // COMMANDLINEOPTION: Game: -contagiontheory runs the game Contagion Theory
78 { GAME_EDU2P,                                   GAME_EDU2P,                                     "edu2p",                                "-edu2p",                                       "EDU2 Prototype",                       "EDU2_Prototype",                       "id1",          "edu2",                 "edu2_p",                       "edu2prototype"                 }, // COMMANDLINEOPTION: Game: -edu2p runs the game Edu2 prototype
79 { GAME_PROPHECY,                                GAME_PROPHECY,                          "prophecy",                             "-prophecy",                            "Prophecy",                                     "Prophecy",                                     "gamedata",     NULL,                   "phcy",                         "prophecy"                              }, // COMMANDLINEOPTION: Game: -prophecy runs the game Prophecy
80 { GAME_BLOODOMNICIDE,                   GAME_BLOODOMNICIDE,                     "omnicide",                             "-omnicide",                            "Blood Omnicide",                       "Blood_Omnicide",                       "kain",         NULL,                   "omnicide",                     "omnicide"                              }, // COMMANDLINEOPTION: Game: -omnicide runs the game Blood Omnicide
81 { GAME_STEELSTORM,                              GAME_STEELSTORM,                        "steelstorm",                   "-steelstorm",                          "Steel-Storm",                          "Steel-Storm",                          "gamedata",     NULL,                   "ss",                           "steelstorm"                    }, // COMMANDLINEOPTION: Game: -steelstorm runs the game Steel Storm
82 { 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
83 { 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.
84 { 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
85 { 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
86 { 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
87 { GAME_MOONHELM,                                GAME_MOONHELM,                          "moonhelm",                             "-moonhelm",                            "MoonHelm",                                     "MoonHelm",                                     "data",         NULL,                   "mh",                           "moonhelm"                              }, // COMMANDLINEOPTION: Game: -moonhelm runs the game MoonHelm
88 { GAME_VORETOURNAMENT,                  GAME_VORETOURNAMENT,            "voretournament",               "-voretournament",                      "Vore Tournament",                      "Vore_Tournament",                      "data",         NULL,                   "voretournament",       "voretournament"                }, // COMMANDLINEOPTION: Game: -voretournament runs the multiplayer game Vore Tournament
89 { GAME_DOOMBRINGER,                             GAME_DOOMBRINGER,                       "doombringer",                  "-doombringer",                         "DOOMBRINGER",                          "DOOMBRINGER",                          "dbdata",       NULL,                   "doombringer",          "doombringer"                   }, // COMMANDLINEOPTION: Game: -doombringer runs the game DOOMBRINGER
90 };
91
92 static void COM_SetGameType(int index);
93 void COM_InitGameType (void)
94 {
95         char name [MAX_OSPATH];
96         int i;
97         int index = 0;
98
99 #ifdef FORCEGAME
100         COM_ToLowerString(FORCEGAME, name, sizeof (name));
101 #else
102         // check executable filename for keywords, but do it SMARTLY - only check the last path element
103         FS_StripExtension(FS_FileWithoutPath(sys.argv[0]), name, sizeof (name));
104         COM_ToLowerString(name, name, sizeof (name));
105 #endif
106         for (i = 1;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
107                 if (gamemode_info[i].prog_name && gamemode_info[i].prog_name[0] && strstr (name, gamemode_info[i].prog_name))
108                         index = i;
109
110         // check commandline options for keywords
111         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
112                 if (Sys_CheckParm (gamemode_info[i].cmdline))
113                         index = i;
114
115         com_startupgamemode = gamemode_info[index].mode;
116         com_startupgamegroup = gamemode_info[index].group;
117         COM_SetGameType(index);
118 }
119
120 void COM_ChangeGameTypeForGameDirs(void)
121 {
122         int i;
123         int index = -1;
124         // this will not not change the gamegroup
125         // first check if a base game (single gamedir) matches
126         for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
127         {
128                 if (gamemode_info[i].group == com_startupgamegroup && !(gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]))
129                 {
130                         index = i;
131                         break;
132                 }
133         }
134         // now that we have a base game, see if there is a matching derivative game (two gamedirs)
135         if (fs_numgamedirs)
136         {
137                 for (i = 0;i < (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0]));i++)
138                 {
139                         if (gamemode_info[i].group == com_startupgamegroup && (gamemode_info[i].gamedirname2 && gamemode_info[i].gamedirname2[0]) && !strcasecmp(fs_gamedirs[0], gamemode_info[i].gamedirname2))
140                         {
141                                 index = i;
142                                 break;
143                         }
144                 }
145         }
146         // we now have a good guess at which game this is meant to be...
147         if (index >= 0 && gamemode != gamemode_info[index].mode)
148                 COM_SetGameType(index);
149 }
150
151 static void COM_SetGameType(int index)
152 {
153         static char gamenetworkfilternamebuffer[64];
154         int i, t;
155         if (index < 0 || index >= (int)(sizeof (gamemode_info) / sizeof (gamemode_info[0])))
156                 index = 0;
157         gamemode = gamemode_info[index].mode;
158         gamename = gamemode_info[index].gamename;
159         gamenetworkfiltername = gamemode_info[index].gamenetworkfiltername;
160         gamedirname1 = gamemode_info[index].gamedirname1;
161         gamedirname2 = gamemode_info[index].gamedirname2;
162         gamescreenshotname = gamemode_info[index].gamescreenshotname;
163         gameuserdirname = gamemode_info[index].gameuserdirname;
164
165         if (gamemode == com_startupgamemode)
166         {
167                 if((t = Sys_CheckParm("-customgamename")) && t + 1 < sys.argc)
168                         gamename = gamenetworkfiltername = sys.argv[t+1];
169                 if((t = Sys_CheckParm("-customgamenetworkfiltername")) && t + 1 < sys.argc)
170                         gamenetworkfiltername = sys.argv[t+1];
171                 if((t = Sys_CheckParm("-customgamedirname1")) && t + 1 < sys.argc)
172                         gamedirname1 = sys.argv[t+1];
173                 if((t = Sys_CheckParm("-customgamedirname2")) && t + 1 < sys.argc)
174                         gamedirname2 = *sys.argv[t+1] ? sys.argv[t+1] : NULL;
175                 if((t = Sys_CheckParm("-customgamescreenshotname")) && t + 1 < sys.argc)
176                         gamescreenshotname = sys.argv[t+1];
177                 if((t = Sys_CheckParm("-customgameuserdirname")) && t + 1 < sys.argc)
178                         gameuserdirname = sys.argv[t+1];
179         }
180
181         if (gamedirname2 && gamedirname2[0])
182                 Con_Printf("Game is %s using base gamedirs %s %s", gamename, gamedirname1, gamedirname2);
183         else
184                 Con_Printf("Game is %s using base gamedir %s", gamename, gamedirname1);
185         for (i = 0;i < fs_numgamedirs;i++)
186         {
187                 if (i == 0)
188                         Con_Printf(", with mod gamedirs");
189                 Con_Printf(" %s", fs_gamedirs[i]);
190         }
191         Con_Printf("\n");
192
193         if (strchr(gamenetworkfiltername, ' '))
194         {
195                 char *s;
196                 // if there are spaces in the game's network filter name it would
197                 // cause parse errors in getservers in dpmaster, so we need to replace
198                 // them with _ characters
199                 strlcpy(gamenetworkfilternamebuffer, gamenetworkfiltername, sizeof(gamenetworkfilternamebuffer));
200                 while ((s = strchr(gamenetworkfilternamebuffer, ' ')) != NULL)
201                         *s = '_';
202                 gamenetworkfiltername = gamenetworkfilternamebuffer;
203         }
204
205         Con_Printf("gamename for server filtering: %s\n", gamenetworkfiltername);
206 }