1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
42 #define MAX_BASE_PATHS 10
43 #define MAX_GAME_PATHS 10
44 #define MAX_PAK_PATHS 200
46 qboolean customHomePath = qfalse;
50 char *macLibraryApplicationSupportPath;
53 #if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
54 char *xdgDataHomePath;
55 #endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
57 char installPath[ MAX_OS_PATH ];
60 char *basePaths[ MAX_BASE_PATHS ];
62 char *gamePaths[ MAX_GAME_PATHS ];
64 char *pakPaths[ MAX_PAK_PATHS ];
65 char *homeBasePath = NULL;
69 some of this code is based off the original q3map port from loki
70 and finds various paths. moved here from bsp.c for clarity.
75 gets the user's home dir (for ~/.q3a)
78 char *LokiGetHomeDir( void ){
81 #else // !GDEF_OS_WINDOWS
82 static char buf[ 4096 ];
83 struct passwd pw, *pwp;
86 /* get the home environment variable */
87 home = getenv( "HOME" );
89 /* look up home dir in password database */
92 if ( getpwuid_r( getuid(), &pw, buf, sizeof( buf ), &pwp ) == 0 ) {
106 initializes some paths on linux/os x
109 void LokiInitPaths( char *argv0 ){
112 if ( homePath == NULL ) {
114 home = LokiGetHomeDir();
115 if ( home == NULL ) {
127 char *subPath = "/Library/Application Support";
128 macLibraryApplicationSupportPath = safe_malloc( sizeof( char ) * ( strlen( home ) + strlen( subPath ) ) );
129 sprintf( macLibraryApplicationSupportPath, "%s%s", home, subPath );
130 #endif // GDEF_OS_MACOS
132 #if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
133 xdgDataHomePath = getenv( "XDG_DATA_HOME" );
135 if ( xdgDataHomePath == NULL ) {
136 char *subPath = "/.local/share";
137 xdgDataHomePath = safe_malloc( sizeof( char ) * ( strlen( home ) + strlen( subPath ) ) );
138 sprintf( xdgDataHomePath, "%s%s", home, subPath );
140 #endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
143 /* this is kinda crap, but hey */
144 strcpy( installPath, "../" );
145 #else // !GDEF_OS_WINDOWS
147 char temp[ MAX_OS_PATH ];
153 path = getenv( "PATH" );
155 /* do some path divining */
156 Q_strncpyz( temp, argv0, sizeof( temp ) );
157 if ( strrchr( temp, '/' ) ) {
158 argv0 = strrchr( argv0, '/' ) + 1;
160 else if ( path != NULL ) {
163 This code has a special behavior when q3map2 is a symbolic link.
165 For each dir in ${PATH} (example: "/usr/bin", "/usr/local/bin" if ${PATH} == "/usr/bin:/usr/local/bin"),
166 it looks for "${dir}/q3map2" (file exists and is executable),
167 then it uses "dirname(realpath("${dir}/q3map2"))/../" as installPath.
169 So, if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
170 it will find the installPath "/usr/share/radiant/",
171 so q3map2 will look for "/opt/radiant/baseq3" to find paks.
173 More precisely, it looks for "${dir}/${argv[0]}",
174 so if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
175 and if "/opt/radiant/tools/q3ma2" is a symbolic link to "/opt/radiant/tools/q3map2.x86_64",
176 it will use "dirname("/opt/radiant/tools/q3map2.x86_64")/../" as path,
177 so it will use "/opt/radiant/" as installPath, which will be expanded later to "/opt/radiant/baseq3" to find paks.
183 /* go through each : segment of path */
184 while ( last[ 0 ] != '\0' && found == qfalse )
189 /* find next chunk */
190 last = strchr( path, ':' );
191 if ( last == NULL ) {
192 last = path + strlen( path );
195 /* found home dir candidate */
196 if ( *path == '~' ) {
197 Q_strncpyz( temp, home, sizeof( temp ) );
202 if ( last > ( path + 1 ) ) {
203 // +1 hack: Q_strncat calls Q_strncpyz that expects a len including '\0'
204 // so that extraneous char will be rewritten by '\0', so it's ok.
205 // Also, in this case this extraneous char is always ':' or '\0', so it's ok.
206 Q_strncat( temp, sizeof( temp ), path, ( last - path + 1) );
207 Q_strcat( temp, sizeof( temp ), "/" );
209 Q_strcat( temp, sizeof( temp ), argv0 );
211 /* verify the path */
212 if ( access( temp, X_OK ) == 0 ) {
220 if ( realpath( temp, installPath ) ) {
222 if "q3map2" is "/opt/radiant/tools/q3map2",
223 installPath is "/opt/radiant"
225 *( strrchr( installPath, '/' ) ) = '\0';
226 *( strrchr( installPath, '/' ) ) = '\0';
228 #endif // !GDEF_OS_WINDOWS
235 cleans a dos path \ -> /
238 void CleanPath( char *path ){
241 if ( *path == '\\' ) {
252 gets the game_t based on a -game argument
253 returns NULL if no match found
256 game_t *GetGame( char *arg ){
261 if ( arg == NULL || arg[ 0 ] == '\0' ) {
266 if ( !Q_stricmp( arg, "quake1" ) ||
267 !Q_stricmp( arg, "quake2" ) ||
268 !Q_stricmp( arg, "unreal" ) ||
269 !Q_stricmp( arg, "ut2k3" ) ||
270 !Q_stricmp( arg, "dn3d" ) ||
271 !Q_stricmp( arg, "dnf" ) ||
272 !Q_stricmp( arg, "hl" ) ) {
273 Sys_Printf( "April fools, silly rabbit!\n" );
279 while ( games[ i ].arg != NULL )
281 if ( Q_stricmp( arg, games[ i ].arg ) == 0 ) {
287 /* no matching game */
294 AddBasePath() - ydnar
295 adds a base path to the list
298 void AddBasePath( char *path ){
300 if ( path == NULL || path[ 0 ] == '\0' || numBasePaths >= MAX_BASE_PATHS ) {
304 /* add it to the list */
305 basePaths[ numBasePaths ] = safe_malloc( strlen( path ) + 1 );
306 strcpy( basePaths[ numBasePaths ], path );
307 CleanPath( basePaths[ numBasePaths ] );
314 AddHomeBasePath() - ydnar
315 adds a base path to the beginning of the list
318 void AddHomeBasePath( char *path ){
320 char temp[ MAX_OS_PATH ];
322 if ( homePath == NULL ) {
327 if ( path == NULL || path[ 0 ] == '\0' ) {
331 if ( strcmp( path, "." ) == 0 ) {
332 /* -fs_homebase . means that -fs_home is to be used as is */
333 strcpy( temp, homePath );
337 tempHomePath = homePath;
339 /* homePath is . on Windows if not user supplied */
343 use ${HOME}/Library/Application as ${HOME}
344 if home path is not user supplied
345 and strip the leading dot from prefix in any case
347 basically it produces
348 ${HOME}/Library/Application/unvanquished
349 /user/supplied/home/path/unvanquished
351 tempHomePath = macLibraryApplicationSupportPath;
353 #endif // GDEF_OS_MACOS
355 #if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
357 on Linux, check if game uses ${XDG_DATA_HOME}/prefix instead of ${HOME}/.prefix
358 if yes and home path is not user supplied
359 use XDG_DATA_HOME instead of HOME
360 and strip the leading dot
362 basically it produces
363 ${XDG_DATA_HOME}/unvanquished
364 /user/supplied/home/path/unvanquished
368 /user/supplied/home/path/.q3a
371 sprintf( temp, "%s/%s", xdgDataHomePath, ( path + 1 ) );
372 if ( access( temp, X_OK ) == 0 ) {
373 if ( customHomePath == qfalse ) {
374 tempHomePath = xdgDataHomePath;
378 #endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
380 /* concatenate home dir and path */
381 sprintf( temp, "%s/%s", tempHomePath, path );
385 for ( i = ( MAX_BASE_PATHS - 2 ); i >= 0; i-- )
386 basePaths[ i + 1 ] = basePaths[ i ];
388 /* add it to the list */
389 basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
390 strcpy( basePaths[ 0 ], temp );
391 CleanPath( basePaths[ 0 ] );
398 AddGamePath() - ydnar
399 adds a game path to the list
402 void AddGamePath( char *path ){
406 if ( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS ) {
410 /* add it to the list */
411 gamePaths[ numGamePaths ] = safe_malloc( strlen( path ) + 1 );
412 strcpy( gamePaths[ numGamePaths ], path );
413 CleanPath( gamePaths[ numGamePaths ] );
416 /* don't add it if it's already there */
417 for ( i = 0; i < numGamePaths - 1; i++ )
419 if ( strcmp( gamePaths[i], gamePaths[numGamePaths - 1] ) == 0 ) {
420 free( gamePaths[numGamePaths - 1] );
421 gamePaths[numGamePaths - 1] = NULL;
432 adds a pak path to the list
435 void AddPakPath( char *path ){
437 if ( path == NULL || path[ 0 ] == '\0' || numPakPaths >= MAX_PAK_PATHS ) {
441 /* add it to the list */
442 pakPaths[ numPakPaths ] = safe_malloc( strlen( path ) + 1 );
443 strcpy( pakPaths[ numPakPaths ], path );
444 CleanPath( pakPaths[ numPakPaths ] );
452 cleaned up some of the path initialization code from bsp.c
453 will remove any arguments it uses
456 void InitPaths( int *argc, char **argv ){
457 int i, j, k, len, len2;
458 char temp[ MAX_OS_PATH ];
465 Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
467 /* get the install path for backup */
468 LokiInitPaths( argv[ 0 ] );
470 /* set game to default (q3a) */
475 /* parse through the arguments and extract those relevant to paths */
476 for ( i = 0; i < *argc; i++ )
479 if ( argv[ i ] == NULL ) {
484 if ( strcmp( argv[ i ], "-game" ) == 0 ) {
485 if ( ++i >= *argc ) {
486 Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
488 argv[ i - 1 ] = NULL;
489 game = GetGame( argv[ i ] );
490 if ( game == NULL ) {
496 /* -fs_forbiddenpath */
497 else if ( strcmp( argv[ i ], "-fs_forbiddenpath" ) == 0 ) {
498 if ( ++i >= *argc ) {
499 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
501 argv[ i - 1 ] = NULL;
502 if ( g_numForbiddenDirs < VFS_MAXDIRS ) {
503 strncpy( g_strForbiddenDirs[g_numForbiddenDirs], argv[i], PATH_MAX );
504 g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = 0;
505 ++g_numForbiddenDirs;
511 else if ( strcmp( argv[ i ], "-fs_nobasepath" ) == 0 ) {
513 // we don't want any basepath, neither guessed ones
518 /* -fs_nomagicpath */
519 else if ( strcmp( argv[ i ], "-fs_nomagicpath") == 0) {
525 else if ( strcmp( argv[ i ], "-fs_basepath" ) == 0 ) {
526 if ( ++i >= *argc ) {
527 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
529 argv[ i - 1 ] = NULL;
530 AddBasePath( argv[ i ] );
535 else if ( strcmp( argv[ i ], "-fs_game" ) == 0 ) {
536 if ( ++i >= *argc ) {
537 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
539 argv[ i - 1 ] = NULL;
540 AddGamePath( argv[ i ] );
545 else if ( strcmp( argv[ i ], "-fs_home" ) == 0 ) {
546 if ( ++i >= *argc ) {
547 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
549 argv[ i - 1 ] = NULL;
550 customHomePath = qtrue;
556 else if ( strcmp( argv[ i ], "-fs_nohomepath" ) == 0 ) {
562 else if ( strcmp( argv[ i ], "-fs_homebase" ) == 0 ) {
563 if ( ++i >= *argc ) {
564 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
566 argv[ i - 1 ] = NULL;
567 homeBasePath = argv[i];
571 /* -fs_homepath - sets both of them */
572 else if ( strcmp( argv[ i ], "-fs_homepath" ) == 0 ) {
573 if ( ++i >= *argc ) {
574 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
576 argv[ i - 1 ] = NULL;
583 else if ( strcmp( argv[ i ], "-fs_pakpath" ) == 0 ) {
584 if ( ++i >= *argc ) {
585 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
587 argv[ i - 1 ] = NULL;
588 AddPakPath( argv[ i ] );
593 /* remove processed arguments */
594 for ( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
596 for ( ; j < *argc && argv[ j ] == NULL; j++ ) ;
597 argv[ i ] = argv[ j ];
598 if ( argv[ i ] != NULL ) {
604 /* add standard game path */
605 AddGamePath( game->gamePath );
607 /* if there is no base path set, figure it out unless fs_nomagicpath is set */
608 if ( numBasePaths == 0 && noBasePath == 0 && noMagicPath == 0 ) {
609 /* this is another crappy replacement for SetQdirFromPath() */
610 len2 = strlen( game->magic );
611 for ( i = 0; i < *argc && numBasePaths == 0; i++ )
613 /* extract the arg */
614 strcpy( temp, argv[ i ] );
616 len = strlen( temp );
617 Sys_FPrintf( SYS_VRB, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
619 /* this is slow, but only done once */
620 for ( j = 0; j < ( len - len2 ); j++ )
622 /* check for the game's magic word */
623 if ( Q_strncasecmp( &temp[ j ], game->magic, len2 ) == 0 ) {
624 /* now find the next slash and nuke everything after it */
625 while ( temp[ ++j ] != '/' && temp[ j ] != '\0' ) ;
628 /* add this as a base path */
635 /* add install path */
636 if ( numBasePaths == 0 ) {
637 AddBasePath( installPath );
641 if ( numBasePaths == 0 ) {
642 Error( "Failed to find a valid base path." );
646 if ( noBasePath == 1 ) {
650 if ( noHomePath == 0 ) {
651 /* this only affects unix */
652 if ( homeBasePath ) {
653 AddHomeBasePath( homeBasePath );
656 AddHomeBasePath( game->homeBasePath );
660 /* initialize vfs paths */
661 if ( numBasePaths > MAX_BASE_PATHS ) {
662 numBasePaths = MAX_BASE_PATHS;
664 if ( numGamePaths > MAX_GAME_PATHS ) {
665 numGamePaths = MAX_GAME_PATHS;
668 /* walk the list of game paths */
669 for ( j = 0; j < numGamePaths; j++ )
671 /* walk the list of base paths */
672 for ( i = 0; i < numBasePaths; i++ )
674 /* create a full path and initialize it */
675 sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
676 vfsInitDirectory( temp );
680 /* initialize vfs paths */
681 if ( numPakPaths > MAX_PAK_PATHS ) {
682 numPakPaths = MAX_PAK_PATHS;
685 /* walk the list of pak paths */
686 for ( i = 0; i < numPakPaths; i++ )
688 /* initialize this pak path */
689 vfsInitDirectory( pakPaths[ i ] );