2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant 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. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "environment.h"
23 #include "globaldefs.h"
25 #include "stream/textstream.h"
26 #include "string/string.h"
27 #include "stream/stringstream.h"
28 #include "debugging/debugging.h"
36 void args_init(int argc, char const *argv[])
40 for (i = 1; i < argc; i++) {
41 for (k = i; k < argc; k++) {
49 for (j = i + k; j < argc; j++) {
50 argv[j - k] = argv[j];
60 char const *gamedetect_argv_buffer[1024];
62 void gamedetect_found_game(char const *game, char *path)
67 if (g_argv == gamedetect_argv_buffer) {
71 globalOutputStream() << "Detected game " << game << " in " << path << "\n";
73 sprintf(buf, "-%s-EnginePath", game);
75 gamedetect_argv_buffer[argc++] = "-global-gamefile";
76 gamedetect_argv_buffer[argc++] = game;
77 gamedetect_argv_buffer[argc++] = buf;
78 gamedetect_argv_buffer[argc++] = path;
79 if ((size_t) (argc + g_argc) >= sizeof(gamedetect_argv_buffer) / sizeof(*gamedetect_argv_buffer) - 1) {
80 g_argc = sizeof(gamedetect_argv_buffer) / sizeof(*gamedetect_argv_buffer) - g_argc - 1;
82 memcpy(gamedetect_argv_buffer + 4, g_argv, sizeof(*gamedetect_argv_buffer) * g_argc);
84 g_argv = gamedetect_argv_buffer;
87 bool gamedetect_check_game(char const *gamefile, const char *checkfile1, const char *checkfile2,
88 char *buf /* must have 64 bytes free after bufpos */, int bufpos)
92 strcpy(buf + bufpos + 1, checkfile1);
93 globalOutputStream() << "Checking for a game file in " << buf << "\n";
94 if (!file_exists(buf)) {
99 strcpy(buf + bufpos + 1, checkfile2);
100 globalOutputStream() << "Checking for a game file in " << buf << "\n";
101 if (!file_exists(buf)) {
107 gamedetect_found_game(gamefile, buf);
113 // if we're inside a Nexuiz install
114 // default to nexuiz.game (unless the user used an option to inhibit this)
115 bool nogamedetect = false;
117 for (i = 1; i < g_argc - 1; ++i) {
118 if (g_argv[i][0] == '-') {
119 if (!strcmp(g_argv[i], "-gamedetect")) {
120 nogamedetect = !strcmp(g_argv[i + 1], "false");
126 static char buf[1024 + 64];
127 strncpy(buf, environment_get_app_path(), sizeof(buf));
128 buf[sizeof(buf) - 1 - 64] = 0;
133 char *p = buf + strlen(buf) - 1; // point directly on the slash of get_app_path
135 // TODO add more games to this
137 // try to detect Nexuiz installs
139 if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz.exe", buf, p - buf ) )
141 if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "Nexuiz.app/Contents/Info.plist", buf, p - buf ) )
143 if (gamedetect_check_game("nexuiz.game", "data/common-spog.pk3", "nexuiz-linux-glx.sh", buf, p - buf))
147 // try to detect Quetoo installs
148 if (gamedetect_check_game("quetoo.game", "default/icons/quetoo.png", NULL, buf, p - buf)) {
152 // try to detect Warsow installs
153 if (gamedetect_check_game("warsow.game", "basewsw/dedicated_autoexec.cfg", NULL, buf, p - buf)) {
160 while (p != buf && *p != '/' && *p != '\\') {
168 CopiedString home_path;
169 CopiedString app_path;
172 const char *environment_get_home_path()
174 return home_path.c_str();
177 const char *environment_get_app_path()
179 return app_path.c_str();
182 bool portable_app_setup()
184 StringOutputStream confdir(256);
185 confdir << app_path.c_str() << "settings/";
186 if (file_exists(confdir.c_str())) {
187 home_path = confdir.c_str();
201 const char *LINK_NAME =
204 #else // FreeBSD and OSX
209 /// brief Returns the filename of the executable belonging to the current process, or 0 if not found.
210 char const *getexename(char *buf)
212 /* Now read the symbolic link */
213 int ret = readlink(LINK_NAME, buf, PATH_MAX);
216 globalOutputStream() << "getexename: falling back to argv[0]: " << makeQuoted(g_argv[0]);
217 const char *path = realpath(g_argv[0], buf);
219 /* In case of an error, leave the handling up to the caller */
224 /* Ensure proper NUL termination */
227 /* delete the program name */
228 *(strrchr(buf, '/')) = '\0';
230 // NOTE: we build app path with a trailing '/'
231 // it's a general convention in Radiant to have the slash at the end of directories
232 if (buf[strlen(buf) - 1] != '/') {
239 void environment_init(int argc, char const *argv[])
241 // Give away unnecessary root privileges.
242 // Important: must be done before calling gtk_init().
246 if (geteuid() == 0 && (loginname = getlogin()) != 0 &&
247 (pw = getpwnam(loginname)) != 0) {
251 args_init(argc, argv);
255 app_path = getexename(real);
256 ASSERT_MESSAGE(!string_empty(app_path.c_str()), "failed to deduce app path");
259 if (!portable_app_setup()) {
260 StringOutputStream home(256);
261 home << DirectoryCleaned(g_get_user_config_dir()) << "netradiant/";
262 Q_mkdir(home.c_str());
263 home_path = home.c_str();
268 #elif GDEF_OS_WINDOWS
272 void environment_init( int argc, char const* argv[] ){
273 args_init( argc, argv );
276 // get path to the editor
277 char filename[MAX_PATH + 1];
278 GetModuleFileName( 0, filename, MAX_PATH );
279 char* last_separator = strrchr( filename, '\\' );
280 if ( last_separator != 0 ) {
281 *( last_separator + 1 ) = '\0';
287 StringOutputStream app( 256 );
288 app << PathCleaned( filename );
289 app_path = app.c_str();
292 if ( !portable_app_setup() ) {
293 char *appdata = getenv( "APPDATA" );
294 StringOutputStream home( 256 );
295 if ( !appdata || string_empty( appdata ) ) {
296 ERROR_MESSAGE( "Application Data folder not available.\n"
297 "Radiant will use C:\\ for user preferences.\n" );
302 home << PathCleaned( appdata );
304 home << "/NetRadiantSettings/";
305 Q_mkdir( home.c_str() );
306 home_path = home.c_str();
312 #error "unsupported platform"