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
47 char installPath[ MAX_OS_PATH ];
50 char *basePaths[ MAX_BASE_PATHS ];
52 char *gamePaths[ MAX_GAME_PATHS ];
54 char *pakPaths[ MAX_PAK_PATHS ];
55 char *homeBasePath = NULL;
59 some of this code is based off the original q3map port from loki
60 and finds various paths. moved here from bsp.c for clarity.
65 gets the user's home dir (for ~/.q3a)
68 char *LokiGetHomeDir( void ){
72 static char buf[ 4096 ];
73 struct passwd pw, *pwp;
75 static char homeBuf[MAX_OS_PATH];
78 /* get the home environment variable */
79 home = getenv( "HOME" );
81 /* look up home dir in password database */
84 if ( getpwuid_r( getuid(), &pw, buf, sizeof( buf ), &pwp ) == 0 ) {
89 snprintf( homeBuf, sizeof( homeBuf ), "%s/.", home );
100 initializes some paths on linux/os x
103 void LokiInitPaths( char *argv0 ){
108 home = LokiGetHomeDir();
109 if ( home == NULL ) {
121 /* this is kinda crap, but hey */
122 strcpy( installPath, "../" );
125 char temp[ MAX_OS_PATH ];
131 path = getenv( "PATH" );
133 /* do some path divining */
134 Q_strncpyz( temp, argv0, sizeof( temp ) );
135 if ( strrchr( temp, '/' ) ) {
136 argv0 = strrchr( argv0, '/' ) + 1;
141 This code has a special behavior when q3map2 is a symbolic link.
143 For each dir in ${PATH} (example: "/usr/bin", "/usr/local/bin" if ${PATH} == "/usr/bin:/usr/local/bin"),
144 it looks for "${dir}/q3map2" (file exists and is executable),
145 then it uses "dirname(realpath("${dir}/q3map2"))/../" as installPath.
147 So, if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
148 it will find the installPath "/usr/share/radiant/",
149 so q3map2 will look for "/opt/radiant/baseq3" to find paks.
151 More precisely, it looks for "${dir}/${argv[0]}",
152 so if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
153 and if "/opt/radiant/tools/q3ma2" is a symbolic link to "/opt/radiant/tools/q3map2.x86_64",
154 it will use "dirname("/opt/radiant/tools/q3map2.x86_64")/../" as path,
155 so it will use "/opt/radiant/" as installPath, which will be expanded later to "/opt/radiant/baseq3" to find paks.
161 /* go through each : segment of path */
162 while ( last[ 0 ] != '\0' && found == qfalse )
167 /* find next chunk */
168 last = strchr( path, ':' );
169 if ( last == NULL ) {
170 last = path + strlen( path );
173 /* found home dir candidate */
174 if ( *path == '~' ) {
175 Q_strncpyz( temp, home, sizeof( temp ) );
181 if ( last > ( path + 1 ) ) {
182 // +1 hack: Q_strncat calls Q_strncpyz that expects a len including '\0'
183 // so that extraneous char will be rewritten by '\0', so it's ok.
184 // Also, in this case this extraneous char is always ':' or '\0', so it's ok.
185 Q_strncat( temp, sizeof( temp ), path, ( last - path + 1) );
186 Q_strcat( temp, sizeof( temp ), "/" );
188 Q_strcat( temp, sizeof( temp ), argv0 );
190 /* verify the path */
191 if ( access( temp, X_OK ) == 0 ) {
199 if ( realpath( temp, installPath ) ) {
201 if "q3map2" is "/opt/radiant/tools/q3map2",
202 installPath is "/opt/radiant"
204 *( strrchr( installPath, '/' ) ) = '\0';
205 *( strrchr( installPath, '/' ) ) = '\0';
214 cleans a dos path \ -> /
217 void CleanPath( char *path ){
220 if ( *path == '\\' ) {
231 gets the game_t based on a -game argument
232 returns NULL if no match found
235 game_t *GetGame( char *arg ){
240 if ( arg == NULL || arg[ 0 ] == '\0' ) {
245 if ( !Q_stricmp( arg, "quake1" ) ||
246 !Q_stricmp( arg, "quake2" ) ||
247 !Q_stricmp( arg, "unreal" ) ||
248 !Q_stricmp( arg, "ut2k3" ) ||
249 !Q_stricmp( arg, "dn3d" ) ||
250 !Q_stricmp( arg, "dnf" ) ||
251 !Q_stricmp( arg, "hl" ) ) {
252 Sys_Printf( "April fools, silly rabbit!\n" );
258 while ( games[ i ].arg != NULL )
260 if ( Q_stricmp( arg, games[ i ].arg ) == 0 ) {
266 /* no matching game */
273 AddBasePath() - ydnar
274 adds a base path to the list
277 void AddBasePath( char *path ){
279 if ( path == NULL || path[ 0 ] == '\0' || numBasePaths >= MAX_BASE_PATHS ) {
283 /* add it to the list */
284 basePaths[ numBasePaths ] = safe_malloc( strlen( path ) + 1 );
285 strcpy( basePaths[ numBasePaths ], path );
286 CleanPath( basePaths[ numBasePaths ] );
287 if ( EnginePath[0] == '\0' ) strcpy( EnginePath, basePaths[ numBasePaths ] );
294 AddHomeBasePath() - ydnar
295 adds a base path to the beginning of the list, prefixed by ~/
298 void AddHomeBasePath( char *path ){
300 char temp[ MAX_OS_PATH ];
308 if ( path == NULL || path[ 0 ] == '\0' ) {
312 /* strip leading dot, if homePath does not end in /. */
313 homePathLen = strlen( homePath );
314 if ( !strcmp( path, "." ) ) {
315 /* -fs_homebase . means that -fs_home is to be used as is */
316 strcpy( temp, homePath );
318 else if ( homePathLen >= 2 && !strcmp( homePath + homePathLen - 2, "/." ) ) {
319 /* remove trailing /. of homePath */
322 /* concatenate home dir and path */
323 sprintf( temp, "%.*s/%s", homePathLen, homePath, path );
327 /* remove leading . of path */
328 if ( path[0] == '.' ) {
332 /* concatenate home dir and path */
333 sprintf( temp, "%s/%s", homePath, path );
337 for ( i = ( MAX_BASE_PATHS - 2 ); i >= 0; i-- )
338 basePaths[ i + 1 ] = basePaths[ i ];
340 /* add it to the list */
341 basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
342 strcpy( basePaths[ 0 ], temp );
343 CleanPath( basePaths[ 0 ] );
350 AddGamePath() - ydnar
351 adds a game path to the list
354 void AddGamePath( char *path ){
358 if ( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS ) {
362 /* add it to the list */
363 gamePaths[ numGamePaths ] = safe_malloc( strlen( path ) + 1 );
364 strcpy( gamePaths[ numGamePaths ], path );
365 CleanPath( gamePaths[ numGamePaths ] );
368 /* don't add it if it's already there */
369 for ( i = 0; i < numGamePaths - 1; i++ )
371 if ( strcmp( gamePaths[i], gamePaths[numGamePaths - 1] ) == 0 ) {
372 free( gamePaths[numGamePaths - 1] );
373 gamePaths[numGamePaths - 1] = NULL;
384 adds a pak path to the list
387 void AddPakPath( char *path ){
389 if ( path == NULL || path[ 0 ] == '\0' || numPakPaths >= MAX_PAK_PATHS ) {
393 /* add it to the list */
394 pakPaths[ numPakPaths ] = safe_malloc( strlen( path ) + 1 );
395 strcpy( pakPaths[ numPakPaths ], path );
396 CleanPath( pakPaths[ numPakPaths ] );
404 cleaned up some of the path initialization code from bsp.c
405 will remove any arguments it uses
408 void InitPaths( int *argc, char **argv ){
409 int i, j, k, len, len2;
410 char temp[ MAX_OS_PATH ];
417 Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
419 /* get the install path for backup */
420 LokiInitPaths( argv[ 0 ] );
422 /* set game to default (q3a) */
427 EnginePath[0] = '\0';
429 /* parse through the arguments and extract those relevant to paths */
430 for ( i = 0; i < *argc; i++ )
433 if ( argv[ i ] == NULL ) {
438 if ( strcmp( argv[ i ], "-game" ) == 0 ) {
439 if ( ++i >= *argc ) {
440 Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
442 argv[ i - 1 ] = NULL;
443 game = GetGame( argv[ i ] );
444 if ( game == NULL ) {
450 /* -fs_forbiddenpath */
451 else if ( strcmp( argv[ i ], "-fs_forbiddenpath" ) == 0 ) {
452 if ( ++i >= *argc ) {
453 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
455 argv[ i - 1 ] = NULL;
456 if ( g_numForbiddenDirs < VFS_MAXDIRS ) {
457 strncpy( g_strForbiddenDirs[g_numForbiddenDirs], argv[i], PATH_MAX );
458 g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = 0;
459 ++g_numForbiddenDirs;
465 else if ( strcmp( argv[ i ], "-fs_nobasepath" ) == 0 ) {
467 // we don't want any basepath, neither guessed ones
472 /* -fs_nomagicpath */
473 else if ( strcmp( argv[ i ], "-fs_nomagicpath") == 0) {
479 else if ( strcmp( argv[ i ], "-fs_basepath" ) == 0 ) {
480 if ( ++i >= *argc ) {
481 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
483 argv[ i - 1 ] = NULL;
484 AddBasePath( argv[ i ] );
489 else if ( strcmp( argv[ i ], "-fs_game" ) == 0 ) {
490 if ( ++i >= *argc ) {
491 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
493 argv[ i - 1 ] = NULL;
494 AddGamePath( argv[ i ] );
499 else if ( strcmp( argv[ i ], "-fs_home" ) == 0 ) {
500 if ( ++i >= *argc ) {
501 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
503 argv[ i - 1 ] = NULL;
509 else if ( strcmp( argv[ i ], "-fs_nohomepath" ) == 0 ) {
515 else if ( strcmp( argv[ i ], "-fs_homebase" ) == 0 ) {
516 if ( ++i >= *argc ) {
517 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
519 argv[ i - 1 ] = NULL;
520 homeBasePath = argv[i];
524 /* -fs_homepath - sets both of them */
525 else if ( strcmp( argv[ i ], "-fs_homepath" ) == 0 ) {
526 if ( ++i >= *argc ) {
527 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
529 argv[ i - 1 ] = NULL;
536 /* remove processed arguments */
537 for ( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
539 for ( ; j < *argc && argv[ j ] == NULL; j++ ) ;
540 argv[ i ] = argv[ j ];
541 if ( argv[ i ] != NULL ) {
547 /* add standard game path */
548 AddGamePath( game->gamePath );
550 /* if there is no base path set, figure it out unless fs_nomagicpath is set */
551 if ( numBasePaths == 0 && noBasePath == 0 && noMagicPath == 0 ) {
552 /* this is another crappy replacement for SetQdirFromPath() */
553 len2 = strlen( game->magic );
554 for ( i = 0; i < *argc && numBasePaths == 0; i++ )
556 /* extract the arg */
557 strcpy( temp, argv[ i ] );
559 len = strlen( temp );
560 Sys_FPrintf( SYS_VRB, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
562 /* this is slow, but only done once */
563 for ( j = 0; j < ( len - len2 ); j++ )
565 /* check for the game's magic word */
566 if ( Q_strncasecmp( &temp[ j ], game->magic, len2 ) == 0 ) {
567 /* now find the next slash and nuke everything after it */
568 while ( temp[ ++j ] != '/' && temp[ j ] != '\0' ) ;
571 /* add this as a base path */
578 /* add install path */
579 if ( numBasePaths == 0 ) {
580 AddBasePath( installPath );
584 if ( numBasePaths == 0 ) {
585 Error( "Failed to find a valid base path." );
589 if ( noBasePath == 1 ) {
593 if ( noHomePath == 0 ) {
594 /* this only affects unix */
595 if ( homeBasePath ) {
596 AddHomeBasePath( homeBasePath );
599 AddHomeBasePath( game->homeBasePath );
603 /* initialize vfs paths */
604 if ( numBasePaths > MAX_BASE_PATHS ) {
605 numBasePaths = MAX_BASE_PATHS;
607 if ( numGamePaths > MAX_GAME_PATHS ) {
608 numGamePaths = MAX_GAME_PATHS;
611 /* walk the list of game paths */
612 for ( j = 0; j < numGamePaths; j++ )
614 /* walk the list of base paths */
615 for ( i = 0; i < numBasePaths; i++ )
617 /* create a full path and initialize it */
618 sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
619 vfsInitDirectory( temp );
623 /* initialize vfs paths */
624 if ( numPakPaths > MAX_PAK_PATHS ) {
625 numPakPaths = MAX_PAK_PATHS;
628 /* walk the list of pak paths */
629 for ( i = 0; i < numPakPaths; i++ )
631 /* initialize this pak path */
632 vfsInitDirectory( pakPaths[ i ] );