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 ) );
180 if ( last > ( path + 1 ) ) {
181 // +1 hack: Q_strncat calls Q_strncpyz that expects a len including '\0'
182 // so that extraneous char will be rewritten by '\0', so it's ok.
183 // Also, in this case this extraneous char is always ':' or '\0', so it's ok.
184 Q_strncat( temp, sizeof( temp ), path, ( last - path + 1) );
185 Q_strcat( temp, sizeof( temp ), "/" );
187 Q_strcat( temp, sizeof( temp ), argv0 );
189 /* verify the path */
190 if ( access( temp, X_OK ) == 0 ) {
198 if ( realpath( temp, installPath ) ) {
200 if "q3map2" is "/opt/radiant/tools/q3map2",
201 installPath is "/opt/radiant"
203 *( strrchr( installPath, '/' ) ) = '\0';
204 *( strrchr( installPath, '/' ) ) = '\0';
213 cleans a dos path \ -> /
216 void CleanPath( char *path ){
219 if ( *path == '\\' ) {
230 gets the game_t based on a -game argument
231 returns NULL if no match found
234 game_t *GetGame( char *arg ){
239 if ( arg == NULL || arg[ 0 ] == '\0' ) {
244 if ( !Q_stricmp( arg, "quake1" ) ||
245 !Q_stricmp( arg, "quake2" ) ||
246 !Q_stricmp( arg, "unreal" ) ||
247 !Q_stricmp( arg, "ut2k3" ) ||
248 !Q_stricmp( arg, "dn3d" ) ||
249 !Q_stricmp( arg, "dnf" ) ||
250 !Q_stricmp( arg, "hl" ) ) {
251 Sys_Printf( "April fools, silly rabbit!\n" );
257 while ( games[ i ].arg != NULL )
259 if ( Q_stricmp( arg, games[ i ].arg ) == 0 ) {
265 /* no matching game */
272 AddBasePath() - ydnar
273 adds a base path to the list
276 void AddBasePath( char *path ){
278 if ( path == NULL || path[ 0 ] == '\0' || numBasePaths >= MAX_BASE_PATHS ) {
282 /* add it to the list */
283 basePaths[ numBasePaths ] = safe_malloc( strlen( path ) + 1 );
284 strcpy( basePaths[ numBasePaths ], path );
285 CleanPath( basePaths[ numBasePaths ] );
286 if ( EnginePath[0] == '\0' ) strcpy( EnginePath, basePaths[ numBasePaths ] );
293 AddHomeBasePath() - ydnar
294 adds a base path to the beginning of the list, prefixed by ~/
297 void AddHomeBasePath( char *path ){
299 char temp[ MAX_OS_PATH ];
307 if ( path == NULL || path[ 0 ] == '\0' ) {
311 /* strip leading dot, if homePath does not end in /. */
312 homePathLen = strlen( homePath );
313 if ( !strcmp( path, "." ) ) {
314 /* -fs_homebase . means that -fs_home is to be used as is */
315 strcpy( temp, homePath );
317 else if ( homePathLen >= 2 && !strcmp( homePath + homePathLen - 2, "/." ) ) {
318 /* remove trailing /. of homePath */
321 /* concatenate home dir and path */
322 sprintf( temp, "%.*s/%s", homePathLen, homePath, path );
326 /* remove leading . of path */
327 if ( path[0] == '.' ) {
331 /* concatenate home dir and path */
332 sprintf( temp, "%s/%s", homePath, path );
336 for ( i = ( MAX_BASE_PATHS - 2 ); i >= 0; i-- )
337 basePaths[ i + 1 ] = basePaths[ i ];
339 /* add it to the list */
340 basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
341 strcpy( basePaths[ 0 ], temp );
342 CleanPath( basePaths[ 0 ] );
349 AddGamePath() - ydnar
350 adds a game path to the list
353 void AddGamePath( char *path ){
357 if ( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS ) {
361 /* add it to the list */
362 gamePaths[ numGamePaths ] = safe_malloc( strlen( path ) + 1 );
363 strcpy( gamePaths[ numGamePaths ], path );
364 CleanPath( gamePaths[ numGamePaths ] );
367 /* don't add it if it's already there */
368 for ( i = 0; i < numGamePaths - 1; i++ )
370 if ( strcmp( gamePaths[i], gamePaths[numGamePaths - 1] ) == 0 ) {
371 free( gamePaths[numGamePaths - 1] );
372 gamePaths[numGamePaths - 1] = NULL;
383 adds a pak path to the list
386 void AddPakPath( char *path ){
388 if ( path == NULL || path[ 0 ] == '\0' || numPakPaths >= MAX_PAK_PATHS ) {
392 /* add it to the list */
393 pakPaths[ numPakPaths ] = safe_malloc( strlen( path ) + 1 );
394 strcpy( pakPaths[ numPakPaths ], path );
395 CleanPath( pakPaths[ numPakPaths ] );
403 cleaned up some of the path initialization code from bsp.c
404 will remove any arguments it uses
407 void InitPaths( int *argc, char **argv ){
408 int i, j, k, len, len2;
409 char temp[ MAX_OS_PATH ];
416 Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
418 /* get the install path for backup */
419 LokiInitPaths( argv[ 0 ] );
421 /* set game to default (q3a) */
426 EnginePath[0] = '\0';
428 /* parse through the arguments and extract those relevant to paths */
429 for ( i = 0; i < *argc; i++ )
432 if ( argv[ i ] == NULL ) {
437 if ( strcmp( argv[ i ], "-game" ) == 0 ) {
438 if ( ++i >= *argc ) {
439 Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
441 argv[ i - 1 ] = NULL;
442 game = GetGame( argv[ i ] );
443 if ( game == NULL ) {
449 /* -fs_forbiddenpath */
450 else if ( strcmp( argv[ i ], "-fs_forbiddenpath" ) == 0 ) {
451 if ( ++i >= *argc ) {
452 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
454 argv[ i - 1 ] = NULL;
455 if ( g_numForbiddenDirs < VFS_MAXDIRS ) {
456 strncpy( g_strForbiddenDirs[g_numForbiddenDirs], argv[i], PATH_MAX );
457 g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = 0;
458 ++g_numForbiddenDirs;
464 else if ( strcmp( argv[ i ], "-fs_nobasepath" ) == 0 ) {
466 // we don't want any basepath, neither guessed ones
471 /* -fs_nomagicpath */
472 else if ( strcmp( argv[ i ], "-fs_nomagicpath") == 0) {
478 else if ( strcmp( argv[ i ], "-fs_basepath" ) == 0 ) {
479 if ( ++i >= *argc ) {
480 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
482 argv[ i - 1 ] = NULL;
483 AddBasePath( argv[ i ] );
488 else if ( strcmp( argv[ i ], "-fs_game" ) == 0 ) {
489 if ( ++i >= *argc ) {
490 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
492 argv[ i - 1 ] = NULL;
493 AddGamePath( argv[ i ] );
498 else if ( strcmp( argv[ i ], "-fs_home" ) == 0 ) {
499 if ( ++i >= *argc ) {
500 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
502 argv[ i - 1 ] = NULL;
508 else if ( strcmp( argv[ i ], "-fs_nohomepath" ) == 0 ) {
514 else if ( strcmp( argv[ i ], "-fs_homebase" ) == 0 ) {
515 if ( ++i >= *argc ) {
516 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
518 argv[ i - 1 ] = NULL;
519 homeBasePath = argv[i];
523 /* -fs_homepath - sets both of them */
524 else if ( strcmp( argv[ i ], "-fs_homepath" ) == 0 ) {
525 if ( ++i >= *argc ) {
526 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
528 argv[ i - 1 ] = NULL;
535 else if ( strcmp( argv[ i ], "-fs_pakpath" ) == 0 ) {
536 if ( ++i >= *argc ) {
537 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
539 argv[ i - 1 ] = NULL;
540 AddPakPath( argv[ i ] );
545 /* remove processed arguments */
546 for ( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
548 for ( ; j < *argc && argv[ j ] == NULL; j++ ) ;
549 argv[ i ] = argv[ j ];
550 if ( argv[ i ] != NULL ) {
556 /* add standard game path */
557 AddGamePath( game->gamePath );
559 /* if there is no base path set, figure it out unless fs_nomagicpath is set */
560 if ( numBasePaths == 0 && noBasePath == 0 && noMagicPath == 0 ) {
561 /* this is another crappy replacement for SetQdirFromPath() */
562 len2 = strlen( game->magic );
563 for ( i = 0; i < *argc && numBasePaths == 0; i++ )
565 /* extract the arg */
566 strcpy( temp, argv[ i ] );
568 len = strlen( temp );
569 Sys_FPrintf( SYS_VRB, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
571 /* this is slow, but only done once */
572 for ( j = 0; j < ( len - len2 ); j++ )
574 /* check for the game's magic word */
575 if ( Q_strncasecmp( &temp[ j ], game->magic, len2 ) == 0 ) {
576 /* now find the next slash and nuke everything after it */
577 while ( temp[ ++j ] != '/' && temp[ j ] != '\0' ) ;
580 /* add this as a base path */
587 /* add install path */
588 if ( numBasePaths == 0 ) {
589 AddBasePath( installPath );
593 if ( numBasePaths == 0 ) {
594 Error( "Failed to find a valid base path." );
598 if ( noBasePath == 1 ) {
602 if ( noHomePath == 0 ) {
603 /* this only affects unix */
604 if ( homeBasePath ) {
605 AddHomeBasePath( homeBasePath );
608 AddHomeBasePath( game->homeBasePath );
612 /* initialize vfs paths */
613 if ( numBasePaths > MAX_BASE_PATHS ) {
614 numBasePaths = MAX_BASE_PATHS;
616 if ( numGamePaths > MAX_GAME_PATHS ) {
617 numGamePaths = MAX_GAME_PATHS;
620 /* walk the list of game paths */
621 for ( j = 0; j < numGamePaths; j++ )
623 /* walk the list of base paths */
624 for ( i = 0; i < numBasePaths; i++ )
626 /* create a full path and initialize it */
627 sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
628 vfsInitDirectory( temp );
632 /* initialize vfs paths */
633 if ( numPakPaths > MAX_PAK_PATHS ) {
634 numPakPaths = MAX_PAK_PATHS;
637 /* walk the list of pak paths */
638 for ( i = 0; i < numPakPaths; i++ )
640 /* initialize this pak path */
641 vfsInitDirectory( pakPaths[ i ] );