]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/environment.cpp
Merge branch 'transfilterfix' into 'master'
[xonotic/netradiant.git] / radiant / environment.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #include "environment.h"
23 #include "globaldefs.h"
24
25 #include "stream/textstream.h"
26 #include "string/string.h"
27 #include "stream/stringstream.h"
28 #include "debugging/debugging.h"
29 #include "os/path.h"
30 #include "os/file.h"
31 #include "cmdlib.h"
32
33 int g_argc;
34 char const **g_argv;
35
36 void args_init(int argc, char const *argv[])
37 {
38     int i, j, k;
39
40     for (i = 1; i < argc; i++) {
41         for (k = i; k < argc; k++) {
42             if (argv[k] != 0) {
43                 break;
44             }
45         }
46
47         if (k > i) {
48             k -= i;
49             for (j = i + k; j < argc; j++) {
50                 argv[j - k] = argv[j];
51             }
52             argc -= k;
53         }
54     }
55
56     g_argc = argc;
57     g_argv = argv;
58 }
59
60 char const *gamedetect_argv_buffer[1024];
61
62 void gamedetect_found_game(char const *game, char *path)
63 {
64     int argc;
65     static char buf[128];
66
67     if (g_argv == gamedetect_argv_buffer) {
68         return;
69     }
70
71     globalOutputStream() << "Detected game " << game << " in " << path << "\n";
72
73     sprintf(buf, "-%s-EnginePath", game);
74     argc = 0;
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;
81     }
82     memcpy(gamedetect_argv_buffer + 4, g_argv, sizeof(*gamedetect_argv_buffer) * g_argc);
83     g_argc += argc;
84     g_argv = gamedetect_argv_buffer;
85 }
86
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)
89 {
90     buf[bufpos] = '/';
91
92     strcpy(buf + bufpos + 1, checkfile1);
93     globalOutputStream() << "Checking for a game file in " << buf << "\n";
94     if (!file_exists(buf)) {
95         return false;
96     }
97
98     if (checkfile2) {
99         strcpy(buf + bufpos + 1, checkfile2);
100         globalOutputStream() << "Checking for a game file in " << buf << "\n";
101         if (!file_exists(buf)) {
102             return false;
103         }
104     }
105
106     buf[bufpos + 1] = 0;
107     gamedetect_found_game(gamefile, buf);
108     return true;
109 }
110
111 void gamedetect()
112 {
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;
116     int i;
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");
121             }
122             ++i;
123         }
124     }
125     if (!nogamedetect) {
126         static char buf[1024 + 64];
127         strncpy(buf, environment_get_app_path(), sizeof(buf));
128         buf[sizeof(buf) - 1 - 64] = 0;
129         if (!strlen(buf)) {
130             return;
131         }
132
133         char *p = buf + strlen(buf) - 1; // point directly on the slash of get_app_path
134         while (p != buf) {
135             // TODO add more games to this
136
137             // try to detect Nexuiz installs
138 #if GDEF_OS_WINDOWS
139             if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz.exe", buf, p - buf ) )
140 #elif GDEF_OS_MACOS
141             if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "Nexuiz.app/Contents/Info.plist", buf, p - buf ) )
142 #else
143             if (gamedetect_check_game("nexuiz.game", "data/common-spog.pk3", "nexuiz-linux-glx.sh", buf, p - buf))
144 #endif
145             { return; }
146
147             // try to detect Quetoo installs
148             if (gamedetect_check_game("quetoo.game", "default/icons/quetoo.png", NULL, buf, p - buf)) {
149                 return;
150             }
151
152             // try to detect Warsow installs
153             if (gamedetect_check_game("warsow.game", "basewsw/dedicated_autoexec.cfg", NULL, buf, p - buf)) {
154                 return;
155             }
156
157             // we found nothing
158             // go backwards
159             --p;
160             while (p != buf && *p != '/' && *p != '\\') {
161                 --p;
162             }
163         }
164     }
165 }
166
167 namespace {
168     CopiedString home_path;
169     CopiedString app_path;
170 }
171
172 const char *environment_get_home_path()
173 {
174     return home_path.c_str();
175 }
176
177 const char *environment_get_app_path()
178 {
179     return app_path.c_str();
180 }
181
182 bool portable_app_setup()
183 {
184     StringOutputStream confdir(256);
185     confdir << app_path.c_str() << "settings/";
186     if (file_exists(confdir.c_str())) {
187         home_path = confdir.c_str();
188         return true;
189     }
190     return false;
191 }
192
193 #if GDEF_OS_POSIX
194
195 #include <stdlib.h>
196 #include <pwd.h>
197 #include <unistd.h>
198
199 #include <glib.h>
200
201 const char *LINK_NAME =
202 #if GDEF_OS_LINUX
203         "/proc/self/exe"
204 #else // FreeBSD and OSX
205 "/proc/curproc/file"
206 #endif
207 ;
208
209 /// brief Returns the filename of the executable belonging to the current process, or 0 if not found.
210 char const *getexename(char *buf)
211 {
212     /* Now read the symbolic link */
213     int ret = readlink(LINK_NAME, buf, PATH_MAX);
214
215     if (ret == -1) {
216         globalOutputStream() << "getexename: falling back to argv[0]: " << makeQuoted(g_argv[0]);
217         const char *path = realpath(g_argv[0], buf);
218         if (path == 0) {
219             /* In case of an error, leave the handling up to the caller */
220             return "";
221         }
222     }
223
224     /* Ensure proper NUL termination */
225     buf[ret] = 0;
226
227     /* delete the program name */
228     *(strrchr(buf, '/')) = '\0';
229
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] != '/') {
233         strcat(buf, "/");
234     }
235
236     return buf;
237 }
238
239 void environment_init(int argc, char const *argv[])
240 {
241     // Give away unnecessary root privileges.
242     // Important: must be done before calling gtk_init().
243     char *loginname;
244     struct passwd *pw;
245     seteuid(getuid());
246     if (geteuid() == 0 && (loginname = getlogin()) != 0 &&
247         (pw = getpwnam(loginname)) != 0) {
248         setuid(pw->pw_uid);
249     }
250
251     args_init(argc, argv);
252
253     {
254         char real[PATH_MAX];
255         app_path = getexename(real);
256         ASSERT_MESSAGE(!string_empty(app_path.c_str()), "failed to deduce app path");
257     }
258
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();
264     }
265     gamedetect();
266 }
267
268 #elif GDEF_OS_WINDOWS
269
270 #include <windows.h>
271
272 void environment_init( int argc, char const* argv[] ){
273     args_init( argc, argv );
274
275     {
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';
282         }
283         else
284         {
285             filename[0] = '\0';
286         }
287         StringOutputStream app( 256 );
288         app << PathCleaned( filename );
289         app_path = app.c_str();
290     }
291
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" );
298             home << "C:";
299         }
300         else
301         {
302             home << PathCleaned( appdata );
303         }
304         home << "/NetRadiantSettings/";
305         Q_mkdir( home.c_str() );
306         home_path = home.c_str();
307     }
308     gamedetect();
309 }
310
311 #else
312 #error "unsupported platform"
313 #endif