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, "../" );
124 char temp[ MAX_OS_PATH ];
130 path = getenv( "PATH" );
132 /* do some path divining */
133 Q_strncpyz( temp, argv0, sizeof( temp ) );
134 if ( strrchr( temp, '/' ) ) {
135 argv0 = strrchr( argv0, '/' ) + 1;
140 This code has a special behavior when q3map2 is a symbolic link.
142 For each dir in ${PATH} (example: "/usr/bin", "/usr/local/bin" if ${PATH} == "/usr/bin:/usr/local/bin"),
143 it looks for "${dir}/q3map2" (file exists and is executable),
144 then it uses "dirname(realpath("${dir}/q3map2"))/../" as installPath.
146 So, if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
147 it will find the installPath "/usr/share/radiant/",
148 so q3map2 will look for "/opt/radiant/baseq3" to find paks.
150 More precisely, it looks for "${dir}/${argv[0]}",
151 so if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
152 and if "/opt/radiant/tools/q3ma2" is a symbolic link to "/opt/radiant/tools/q3map2.x86_64",
153 it will use "dirname("/opt/radiant/tools/q3map2.x86_64")/../" as path,
154 so it will use "/opt/radiant/" as installPath, which will be expanded later to "/opt/radiant/baseq3" to find paks.
160 /* go through each : segment of path */
161 while ( last[ 0 ] != '\0' && found == qfalse )
166 /* find next chunk */
167 last = strchr( path, ':' );
168 if ( last == NULL ) {
169 last = path + strlen( path );
172 /* found home dir candidate */
173 if ( *path == '~' ) {
174 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 ] );
292 AddHomeBasePath() - ydnar
293 adds a base path to the beginning of the list, prefixed by ~/
296 void AddHomeBasePath( char *path ){
298 char temp[ MAX_OS_PATH ];
306 if ( path == NULL || path[ 0 ] == '\0' ) {
310 /* strip leading dot, if homePath does not end in /. */
311 homePathLen = strlen( homePath );
312 if ( !strcmp( path, "." ) ) {
313 /* -fs_homebase . means that -fs_home is to be used as is */
314 strcpy( temp, homePath );
316 else if ( homePathLen >= 2 && !strcmp( homePath + homePathLen - 2, "/." ) ) {
317 /* remove trailing /. of homePath */
320 /* concatenate home dir and path */
321 sprintf( temp, "%.*s/%s", homePathLen, homePath, path );
325 /* remove leading . of path */
326 if ( path[0] == '.' ) {
330 /* concatenate home dir and path */
331 sprintf( temp, "%s/%s", homePath, path );
335 for ( i = ( MAX_BASE_PATHS - 2 ); i >= 0; i-- )
336 basePaths[ i + 1 ] = basePaths[ i ];
338 /* add it to the list */
339 basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
340 strcpy( basePaths[ 0 ], temp );
341 CleanPath( basePaths[ 0 ] );
348 AddGamePath() - ydnar
349 adds a game path to the list
352 void AddGamePath( char *path ){
356 if ( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS ) {
360 /* add it to the list */
361 gamePaths[ numGamePaths ] = safe_malloc( strlen( path ) + 1 );
362 strcpy( gamePaths[ numGamePaths ], path );
363 CleanPath( gamePaths[ numGamePaths ] );
366 /* don't add it if it's already there */
367 for ( i = 0; i < numGamePaths - 1; i++ )
369 if ( strcmp( gamePaths[i], gamePaths[numGamePaths - 1] ) == 0 ) {
370 free( gamePaths[numGamePaths - 1] );
371 gamePaths[numGamePaths - 1] = NULL;
382 adds a pak path to the list
385 void AddPakPath( char *path ){
387 if ( path == NULL || path[ 0 ] == '\0' || numPakPaths >= MAX_PAK_PATHS ) {
391 /* add it to the list */
392 pakPaths[ numPakPaths ] = safe_malloc( strlen( path ) + 1 );
393 strcpy( pakPaths[ numPakPaths ], path );
394 CleanPath( pakPaths[ numPakPaths ] );
402 cleaned up some of the path initialization code from bsp.c
403 will remove any arguments it uses
406 void InitPaths( int *argc, char **argv ){
407 int i, j, k, len, len2;
408 char temp[ MAX_OS_PATH ];
415 Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
417 /* get the install path for backup */
418 LokiInitPaths( argv[ 0 ] );
420 /* set game to default (q3a) */
425 /* parse through the arguments and extract those relevant to paths */
426 for ( i = 0; i < *argc; i++ )
429 if ( argv[ i ] == NULL ) {
434 if ( strcmp( argv[ i ], "-game" ) == 0 ) {
435 if ( ++i >= *argc ) {
436 Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
438 argv[ i - 1 ] = NULL;
439 game = GetGame( argv[ i ] );
440 if ( game == NULL ) {
446 /* -fs_forbiddenpath */
447 else if ( strcmp( argv[ i ], "-fs_forbiddenpath" ) == 0 ) {
448 if ( ++i >= *argc ) {
449 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
451 argv[ i - 1 ] = NULL;
452 if ( g_numForbiddenDirs < VFS_MAXDIRS ) {
453 strncpy( g_strForbiddenDirs[g_numForbiddenDirs], argv[i], PATH_MAX );
454 g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = 0;
455 ++g_numForbiddenDirs;
461 else if ( strcmp( argv[ i ], "-fs_nobasepath" ) == 0 ) {
466 /* -fs_nomagicpath */
467 else if ( strcmp( argv[ i ], "-fs_nomagicpath") == 0) {
473 else if ( strcmp( argv[ i ], "-fs_basepath" ) == 0 ) {
474 if ( ++i >= *argc ) {
475 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
477 argv[ i - 1 ] = NULL;
478 AddBasePath( argv[ i ] );
483 else if ( strcmp( argv[ i ], "-fs_game" ) == 0 ) {
484 if ( ++i >= *argc ) {
485 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
487 argv[ i - 1 ] = NULL;
488 AddGamePath( argv[ i ] );
493 else if ( strcmp( argv[ i ], "-fs_home" ) == 0 ) {
494 if ( ++i >= *argc ) {
495 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
497 argv[ i - 1 ] = NULL;
503 else if ( strcmp( argv[ i ], "-fs_nohomepath" ) == 0 ) {
509 else if ( strcmp( argv[ i ], "-fs_homebase" ) == 0 ) {
510 if ( ++i >= *argc ) {
511 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
513 argv[ i - 1 ] = NULL;
514 homeBasePath = argv[i];
518 /* -fs_homepath - sets both of them */
519 else if ( strcmp( argv[ i ], "-fs_homepath" ) == 0 ) {
520 if ( ++i >= *argc ) {
521 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
523 argv[ i - 1 ] = NULL;
530 else if ( strcmp( argv[ i ], "-fs_pakpath" ) == 0 ) {
531 if ( ++i >= *argc ) {
532 Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
534 argv[ i - 1 ] = NULL;
535 AddPakPath( argv[ i ] );
540 /* remove processed arguments */
541 for ( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
543 for ( ; j < *argc && argv[ j ] == NULL; j++ ) ;
544 argv[ i ] = argv[ j ];
545 if ( argv[ i ] != NULL ) {
551 /* add standard game path */
552 AddGamePath( game->gamePath );
554 /* if there is no base path set, figure it out unless fs_nomagicpath is set */
555 if ( numBasePaths == 0 && noBasePath == 0 && noMagicPath == 0 ) {
556 /* this is another crappy replacement for SetQdirFromPath() */
557 len2 = strlen( game->magic );
558 for ( i = 0; i < *argc && numBasePaths == 0; i++ )
560 /* extract the arg */
561 strcpy( temp, argv[ i ] );
563 len = strlen( temp );
564 Sys_FPrintf( SYS_VRB, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
566 /* this is slow, but only done once */
567 for ( j = 0; j < ( len - len2 ); j++ )
569 /* check for the game's magic word */
570 if ( Q_strncasecmp( &temp[ j ], game->magic, len2 ) == 0 ) {
571 /* now find the next slash and nuke everything after it */
572 while ( temp[ ++j ] != '/' && temp[ j ] != '\0' ) ;
575 /* add this as a base path */
582 /* add install path */
583 if ( numBasePaths == 0 ) {
584 AddBasePath( installPath );
588 if ( numBasePaths == 0 ) {
589 Error( "Failed to find a valid base path." );
593 if ( noBasePath == 1 ) {
597 if ( noHomePath == 0 ) {
598 /* this only affects unix */
599 if ( homeBasePath ) {
600 AddHomeBasePath( homeBasePath );
603 AddHomeBasePath( game->homeBasePath );
607 /* initialize vfs paths */
608 if ( numBasePaths > MAX_BASE_PATHS ) {
609 numBasePaths = MAX_BASE_PATHS;
611 if ( numGamePaths > MAX_GAME_PATHS ) {
612 numGamePaths = MAX_GAME_PATHS;
615 /* walk the list of game paths */
616 for ( j = 0; j < numGamePaths; j++ )
618 /* walk the list of base paths */
619 for ( i = 0; i < numBasePaths; i++ )
621 /* create a full path and initialize it */
622 sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
623 vfsInitDirectory( temp );
627 /* initialize vfs paths */
628 if ( numPakPaths > MAX_PAK_PATHS ) {
629 numPakPaths = MAX_PAK_PATHS;
632 /* walk the list of pak paths */
633 for ( i = 0; i < numPakPaths; i++ )
635 /* initialize this pak path */
636 vfsInitDirectory( pakPaths[ i ] );