|
|
1.1.1.4 ! root 1: /* ! 2: Copyright (C) 1997-2001 Id Software, Inc. ! 3: ! 4: This program is free software; you can redistribute it and/or ! 5: modify it under the terms of the GNU General Public License ! 6: as published by the Free Software Foundation; either version 2 ! 7: of the License, or (at your option) any later version. ! 8: ! 9: This program is distributed in the hope that it will be useful, ! 10: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ! 12: ! 13: See the GNU General Public License for more details. ! 14: ! 15: You should have received a copy of the GNU General Public License ! 16: along with this program; if not, write to the Free Software ! 17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ! 18: ! 19: */ 1.1 root 20: 21: #include "qcommon.h" 22: 23: // define this to dissalow any data but the demo pak file 24: //#define NO_ADDONS 25: 26: // if a packfile directory differs from this, it is assumed to be hacked 27: // Full version 28: #define PAK0_CHECKSUM 0x40e614e0 29: // Demo 30: //#define PAK0_CHECKSUM 0xb2c6d7ea 31: // OEM 32: //#define PAK0_CHECKSUM 0x78e135c 33: 34: /* 35: ============================================================================= 36: 37: QUAKE FILESYSTEM 38: 39: ============================================================================= 40: */ 41: 42: 43: // 44: // in memory 45: // 46: 47: typedef struct 48: { 49: char name[MAX_QPATH]; 50: int filepos, filelen; 51: } packfile_t; 52: 53: typedef struct pack_s 54: { 55: char filename[MAX_OSPATH]; 56: FILE *handle; 57: int numfiles; 58: packfile_t *files; 59: } pack_t; 60: 61: char fs_gamedir[MAX_OSPATH]; 62: cvar_t *fs_basedir; 63: cvar_t *fs_cddir; 64: cvar_t *fs_gamedirvar; 65: 66: typedef struct filelink_s 67: { 68: struct filelink_s *next; 69: char *from; 70: int fromlength; 71: char *to; 72: } filelink_t; 73: 74: filelink_t *fs_links; 75: 76: typedef struct searchpath_s 77: { 78: char filename[MAX_OSPATH]; 79: pack_t *pack; // only one of filename / pack will be used 80: struct searchpath_s *next; 81: } searchpath_t; 82: 83: searchpath_t *fs_searchpaths; 84: searchpath_t *fs_base_searchpaths; // without gamedirs 85: 86: 87: /* 88: 89: All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. 90: 91: The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is 92: only used during filesystem initialization. 93: 94: The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. 95: 96: */ 97: 98: 99: /* 100: ================ 101: FS_filelength 102: ================ 103: */ 104: int FS_filelength (FILE *f) 105: { 106: int pos; 107: int end; 108: 109: pos = ftell (f); 110: fseek (f, 0, SEEK_END); 111: end = ftell (f); 112: fseek (f, pos, SEEK_SET); 113: 114: return end; 115: } 116: 117: 118: /* 119: ============ 120: FS_CreatePath 121: 122: Creates any directories needed to store the given filename 123: ============ 124: */ 125: void FS_CreatePath (char *path) 126: { 127: char *ofs; 128: 129: for (ofs = path+1 ; *ofs ; ofs++) 130: { 131: if (*ofs == '/') 132: { // create the directory 133: *ofs = 0; 134: Sys_Mkdir (path); 135: *ofs = '/'; 136: } 137: } 138: } 139: 140: 141: /* 142: ============== 143: FS_FCloseFile 144: 145: For some reason, other dll's can't just cal fclose() 146: on files returned by FS_FOpenFile... 147: ============== 148: */ 149: void FS_FCloseFile (FILE *f) 150: { 151: fclose (f); 152: } 153: 1.1.1.2 root 154: 155: // RAFAEL 156: /* 157: Developer_searchpath 158: */ 159: int Developer_searchpath (int who) 160: { 161: 162: int ch; 1.1.1.3 root 163: // PMM - warning removal 164: // char *start; 1.1.1.2 root 165: searchpath_t *search; 166: 167: if (who == 1) // xatrix 168: ch = 'x'; 1.1.1.3 root 169: else if (who == 2) 170: ch = 'r'; 1.1.1.2 root 171: 172: for (search = fs_searchpaths ; search ; search = search->next) 173: { 1.1.1.3 root 174: if (strstr (search->filename, "xatrix")) 175: return 1; 176: 177: if (strstr (search->filename, "rogue")) 178: return 2; 179: /* 1.1.1.2 root 180: start = strchr (search->filename, ch); 181: 182: if (start == NULL) 183: continue; 184: 185: if (strcmp (start ,"xatrix") == 0) 186: return (1); 1.1.1.3 root 187: */ 1.1.1.2 root 188: } 189: return (0); 190: 191: } 192: 193: 1.1 root 194: /* 195: =========== 196: FS_FOpenFile 197: 198: Finds the file in the search path. 199: returns filesize and an open FILE * 200: Used for streaming data out of either a pak file or 201: a seperate file. 202: =========== 203: */ 1.1.1.2 root 204: int file_from_pak = 0; 1.1 root 205: #ifndef NO_ADDONS 206: int FS_FOpenFile (char *filename, FILE **file) 207: { 208: searchpath_t *search; 209: char netpath[MAX_OSPATH]; 210: pack_t *pak; 211: int i; 212: filelink_t *link; 213: 1.1.1.2 root 214: file_from_pak = 0; 215: 1.1 root 216: // check for links first 217: for (link = fs_links ; link ; link=link->next) 218: { 219: if (!strncmp (filename, link->from, link->fromlength)) 220: { 221: Com_sprintf (netpath, sizeof(netpath), "%s%s",link->to, filename+link->fromlength); 222: *file = fopen (netpath, "rb"); 223: if (*file) 224: { 225: Com_DPrintf ("link file: %s\n",netpath); 226: return FS_filelength (*file); 227: } 228: return -1; 229: } 230: } 231: 232: // 233: // search through the path, one element at a time 234: // 235: for (search = fs_searchpaths ; search ; search = search->next) 236: { 237: // is the element a pak file? 238: if (search->pack) 239: { 240: // look through all the pak file elements 241: pak = search->pack; 242: for (i=0 ; i<pak->numfiles ; i++) 243: if (!Q_strcasecmp (pak->files[i].name, filename)) 244: { // found it! 1.1.1.2 root 245: file_from_pak = 1; 1.1 root 246: Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename); 247: // open a new file on the pakfile 248: *file = fopen (pak->filename, "rb"); 249: if (!*file) 250: Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename); 251: fseek (*file, pak->files[i].filepos, SEEK_SET); 252: return pak->files[i].filelen; 253: } 254: } 255: else 256: { 257: // check a file in the directory tree 258: 259: Com_sprintf (netpath, sizeof(netpath), "%s/%s",search->filename, filename); 260: 261: *file = fopen (netpath, "rb"); 262: if (!*file) 263: continue; 264: 265: Com_DPrintf ("FindFile: %s\n",netpath); 266: 267: return FS_filelength (*file); 268: } 269: 270: } 271: 272: Com_DPrintf ("FindFile: can't find %s\n", filename); 273: 274: *file = NULL; 275: return -1; 276: } 277: 278: #else 279: 280: // this is just for demos to prevent add on hacking 281: 282: int FS_FOpenFile (char *filename, FILE **file) 283: { 284: searchpath_t *search; 285: char netpath[MAX_OSPATH]; 286: pack_t *pak; 287: int i; 288: 1.1.1.2 root 289: file_from_pak = 0; 290: 1.1 root 291: // get config from directory, everything else from pak 292: if (!strcmp(filename, "config.cfg") || !strncmp(filename, "players/", 8)) 293: { 294: Com_sprintf (netpath, sizeof(netpath), "%s/%s",FS_Gamedir(), filename); 295: 296: *file = fopen (netpath, "rb"); 297: if (!*file) 298: return -1; 299: 300: Com_DPrintf ("FindFile: %s\n",netpath); 301: 302: return FS_filelength (*file); 303: } 304: 305: for (search = fs_searchpaths ; search ; search = search->next) 306: if (search->pack) 307: break; 308: if (!search) 309: { 310: *file = NULL; 311: return -1; 312: } 313: 314: pak = search->pack; 315: for (i=0 ; i<pak->numfiles ; i++) 316: if (!Q_strcasecmp (pak->files[i].name, filename)) 317: { // found it! 1.1.1.2 root 318: file_from_pak = 1; 1.1 root 319: Com_DPrintf ("PackFile: %s : %s\n",pak->filename, filename); 320: // open a new file on the pakfile 321: *file = fopen (pak->filename, "rb"); 322: if (!*file) 323: Com_Error (ERR_FATAL, "Couldn't reopen %s", pak->filename); 324: fseek (*file, pak->files[i].filepos, SEEK_SET); 325: return pak->files[i].filelen; 326: } 327: 328: Com_DPrintf ("FindFile: can't find %s\n", filename); 329: 330: *file = NULL; 331: return -1; 332: } 333: 334: #endif 335: 336: 337: /* 338: ================= 339: FS_ReadFile 340: 341: Properly handles partial reads 342: ================= 343: */ 344: void CDAudio_Stop(void); 345: #define MAX_READ 0x10000 // read in blocks of 64k 346: void FS_Read (void *buffer, int len, FILE *f) 347: { 348: int block, remaining; 349: int read; 350: byte *buf; 351: int tries; 352: 353: buf = (byte *)buffer; 354: 355: // read in chunks for progress bar 356: remaining = len; 357: tries = 0; 358: while (remaining) 359: { 360: block = remaining; 361: if (block > MAX_READ) 362: block = MAX_READ; 363: read = fread (buf, 1, block, f); 364: if (read == 0) 365: { 366: // we might have been trying to read from a CD 367: if (!tries) 368: { 369: tries = 1; 370: CDAudio_Stop(); 371: } 372: else 373: Com_Error (ERR_FATAL, "FS_Read: 0 bytes read"); 374: } 375: 376: if (read == -1) 377: Com_Error (ERR_FATAL, "FS_Read: -1 bytes read"); 378: 379: // do some progress bar thing here... 380: 381: remaining -= read; 382: buf += read; 383: } 384: } 385: 386: /* 387: ============ 388: FS_LoadFile 389: 390: Filename are reletive to the quake search path 391: a null buffer will just return the file length without loading 392: ============ 393: */ 394: int FS_LoadFile (char *path, void **buffer) 395: { 396: FILE *h; 397: byte *buf; 398: int len; 399: 400: buf = NULL; // quiet compiler warning 401: 402: // look for it in the filesystem or pack files 403: len = FS_FOpenFile (path, &h); 404: if (!h) 405: { 406: if (buffer) 407: *buffer = NULL; 408: return -1; 409: } 410: 411: if (!buffer) 412: { 413: fclose (h); 414: return len; 415: } 416: 417: buf = Z_Malloc(len); 418: *buffer = buf; 419: 420: FS_Read (buf, len, h); 421: 422: fclose (h); 423: 424: return len; 425: } 426: 427: 428: /* 429: ============= 430: FS_FreeFile 431: ============= 432: */ 433: void FS_FreeFile (void *buffer) 434: { 435: Z_Free (buffer); 436: } 437: 438: /* 439: ================= 440: FS_LoadPackFile 441: 442: Takes an explicit (not game tree related) path to a pak file. 443: 444: Loads the header and directory, adding the files at the beginning 445: of the list so they override previous pack files. 446: ================= 447: */ 448: pack_t *FS_LoadPackFile (char *packfile) 449: { 450: dpackheader_t header; 451: int i; 452: packfile_t *newfiles; 453: int numpackfiles; 454: pack_t *pack; 455: FILE *packhandle; 456: dpackfile_t info[MAX_FILES_IN_PACK]; 457: unsigned checksum; 458: 459: packhandle = fopen(packfile, "rb"); 460: if (!packhandle) 461: return NULL; 462: 463: fread (&header, 1, sizeof(header), packhandle); 464: if (LittleLong(header.ident) != IDPAKHEADER) 465: Com_Error (ERR_FATAL, "%s is not a packfile", packfile); 466: header.dirofs = LittleLong (header.dirofs); 467: header.dirlen = LittleLong (header.dirlen); 468: 469: numpackfiles = header.dirlen / sizeof(dpackfile_t); 470: 471: if (numpackfiles > MAX_FILES_IN_PACK) 472: Com_Error (ERR_FATAL, "%s has %i files", packfile, numpackfiles); 473: 474: newfiles = Z_Malloc (numpackfiles * sizeof(packfile_t)); 475: 476: fseek (packhandle, header.dirofs, SEEK_SET); 477: fread (info, 1, header.dirlen, packhandle); 478: 479: // crc the directory to check for modifications 480: checksum = Com_BlockChecksum ((void *)info, header.dirlen); 481: 482: #ifdef NO_ADDONS 483: if (checksum != PAK0_CHECKSUM) 484: return NULL; 485: #endif 486: // parse the directory 487: for (i=0 ; i<numpackfiles ; i++) 488: { 489: strcpy (newfiles[i].name, info[i].name); 490: newfiles[i].filepos = LittleLong(info[i].filepos); 491: newfiles[i].filelen = LittleLong(info[i].filelen); 492: } 493: 494: pack = Z_Malloc (sizeof (pack_t)); 495: strcpy (pack->filename, packfile); 496: pack->handle = packhandle; 497: pack->numfiles = numpackfiles; 498: pack->files = newfiles; 499: 500: Com_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); 501: return pack; 502: } 503: 504: 505: /* 506: ================ 507: FS_AddGameDirectory 508: 509: Sets fs_gamedir, adds the directory to the head of the path, 510: then loads and adds pak1.pak pak2.pak ... 511: ================ 512: */ 513: void FS_AddGameDirectory (char *dir) 514: { 515: int i; 516: searchpath_t *search; 517: pack_t *pak; 518: char pakfile[MAX_OSPATH]; 519: 520: strcpy (fs_gamedir, dir); 521: 522: // 523: // add the directory to the search path 524: // 525: search = Z_Malloc (sizeof(searchpath_t)); 526: strcpy (search->filename, dir); 527: search->next = fs_searchpaths; 528: fs_searchpaths = search; 529: 530: // 531: // add any pak files in the format pak0.pak pak1.pak, ... 532: // 533: for (i=0; i<10; i++) 534: { 535: Com_sprintf (pakfile, sizeof(pakfile), "%s/pak%i.pak", dir, i); 536: pak = FS_LoadPackFile (pakfile); 537: if (!pak) 538: continue; 539: search = Z_Malloc (sizeof(searchpath_t)); 540: search->pack = pak; 541: search->next = fs_searchpaths; 542: fs_searchpaths = search; 543: } 544: 545: 546: } 547: 548: /* 549: ============ 550: FS_Gamedir 551: 552: Called to find where to write a file (demos, savegames, etc) 553: ============ 554: */ 555: char *FS_Gamedir (void) 556: { 1.1.1.4 ! root 557: if (*fs_gamedir) ! 558: return fs_gamedir; ! 559: else ! 560: return BASEDIRNAME; 1.1 root 561: } 562: 563: /* 564: ============= 565: FS_ExecAutoexec 566: ============= 567: */ 568: void FS_ExecAutoexec (void) 569: { 570: char *dir; 571: char name [MAX_QPATH]; 572: 573: dir = Cvar_VariableString("gamedir"); 574: if (*dir) 575: Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir); 576: else 577: Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME); 578: if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) 579: Cbuf_AddText ("exec autoexec.cfg\n"); 580: Sys_FindClose(); 581: } 582: 583: 584: /* 585: ================ 586: FS_SetGamedir 587: 588: Sets the gamedir and path to a different directory. 589: ================ 590: */ 591: void FS_SetGamedir (char *dir) 592: { 593: searchpath_t *next; 594: 595: if (strstr(dir, "..") || strstr(dir, "/") 596: || strstr(dir, "\\") || strstr(dir, ":") ) 597: { 598: Com_Printf ("Gamedir should be a single filename, not a path\n"); 599: return; 600: } 601: 602: // 603: // free up any current game dir info 604: // 605: while (fs_searchpaths != fs_base_searchpaths) 606: { 607: if (fs_searchpaths->pack) 608: { 609: fclose (fs_searchpaths->pack->handle); 610: Z_Free (fs_searchpaths->pack->files); 611: Z_Free (fs_searchpaths->pack); 612: } 613: next = fs_searchpaths->next; 614: Z_Free (fs_searchpaths); 615: fs_searchpaths = next; 616: } 617: 618: // 619: // flush all data, so it will be forced to reload 620: // 621: if (dedicated && !dedicated->value) 622: Cbuf_AddText ("vid_restart\nsnd_restart\n"); 623: 624: Com_sprintf (fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir); 625: 626: if (!strcmp(dir,BASEDIRNAME) || (*dir == 0)) 627: { 628: Cvar_FullSet ("gamedir", "", CVAR_SERVERINFO|CVAR_NOSET); 629: Cvar_FullSet ("game", "", CVAR_LATCH|CVAR_SERVERINFO); 630: } 631: else 632: { 633: Cvar_FullSet ("gamedir", dir, CVAR_SERVERINFO|CVAR_NOSET); 634: if (fs_cddir->string[0]) 635: FS_AddGameDirectory (va("%s/%s", fs_cddir->string, dir) ); 636: FS_AddGameDirectory (va("%s/%s", fs_basedir->string, dir) ); 637: } 638: } 639: 640: 641: /* 642: ================ 643: FS_Link_f 644: 645: Creates a filelink_t 646: ================ 647: */ 648: void FS_Link_f (void) 649: { 650: filelink_t *l, **prev; 651: 652: if (Cmd_Argc() != 3) 653: { 654: Com_Printf ("USAGE: link <from> <to>\n"); 655: return; 656: } 657: 658: // see if the link already exists 659: prev = &fs_links; 660: for (l=fs_links ; l ; l=l->next) 661: { 662: if (!strcmp (l->from, Cmd_Argv(1))) 663: { 664: Z_Free (l->to); 665: if (!strlen(Cmd_Argv(2))) 666: { // delete it 667: *prev = l->next; 668: Z_Free (l->from); 669: Z_Free (l); 670: return; 671: } 672: l->to = CopyString (Cmd_Argv(2)); 673: return; 674: } 675: prev = &l->next; 676: } 677: 678: // create a new link 679: l = Z_Malloc(sizeof(*l)); 680: l->next = fs_links; 681: fs_links = l; 682: l->from = CopyString(Cmd_Argv(1)); 683: l->fromlength = strlen(l->from); 684: l->to = CopyString(Cmd_Argv(2)); 685: } 686: 687: /* 688: ** FS_ListFiles 689: */ 690: char **FS_ListFiles( char *findname, int *numfiles, unsigned musthave, unsigned canthave ) 691: { 692: char *s; 693: int nfiles = 0; 694: char **list = 0; 695: 696: s = Sys_FindFirst( findname, musthave, canthave ); 697: while ( s ) 698: { 699: if ( s[strlen(s)-1] != '.' ) 700: nfiles++; 701: s = Sys_FindNext( musthave, canthave ); 702: } 703: Sys_FindClose (); 704: 705: if ( !nfiles ) 706: return NULL; 707: 708: nfiles++; // add space for a guard 709: *numfiles = nfiles; 710: 711: list = malloc( sizeof( char * ) * nfiles ); 712: memset( list, 0, sizeof( char * ) * nfiles ); 713: 714: s = Sys_FindFirst( findname, musthave, canthave ); 715: nfiles = 0; 716: while ( s ) 717: { 718: if ( s[strlen(s)-1] != '.' ) 719: { 720: list[nfiles] = strdup( s ); 721: #ifdef _WIN32 722: strlwr( list[nfiles] ); 723: #endif 724: nfiles++; 725: } 726: s = Sys_FindNext( musthave, canthave ); 727: } 728: Sys_FindClose (); 729: 730: return list; 731: } 732: 733: /* 734: ** FS_Dir_f 735: */ 736: void FS_Dir_f( void ) 737: { 738: char *path = NULL; 739: char findname[1024]; 740: char wildcard[1024] = "*.*"; 741: char **dirnames; 742: int ndirs; 743: 744: if ( Cmd_Argc() != 1 ) 745: { 746: strcpy( wildcard, Cmd_Argv( 1 ) ); 747: } 748: 749: while ( ( path = FS_NextPath( path ) ) != NULL ) 750: { 751: char *tmp = findname; 752: 753: Com_sprintf( findname, sizeof(findname), "%s/%s", path, wildcard ); 754: 755: while ( *tmp != 0 ) 756: { 757: if ( *tmp == '\\' ) 758: *tmp = '/'; 759: tmp++; 760: } 761: Com_Printf( "Directory of %s\n", findname ); 762: Com_Printf( "----\n" ); 763: 764: if ( ( dirnames = FS_ListFiles( findname, &ndirs, 0, 0 ) ) != 0 ) 765: { 766: int i; 767: 768: for ( i = 0; i < ndirs-1; i++ ) 769: { 770: if ( strrchr( dirnames[i], '/' ) ) 771: Com_Printf( "%s\n", strrchr( dirnames[i], '/' ) + 1 ); 772: else 773: Com_Printf( "%s\n", dirnames[i] ); 774: 775: free( dirnames[i] ); 776: } 777: free( dirnames ); 778: } 779: Com_Printf( "\n" ); 780: }; 781: } 782: 783: /* 784: ============ 785: FS_Path_f 786: 787: ============ 788: */ 789: void FS_Path_f (void) 790: { 791: searchpath_t *s; 792: filelink_t *l; 793: 794: Com_Printf ("Current search path:\n"); 795: for (s=fs_searchpaths ; s ; s=s->next) 796: { 797: if (s == fs_base_searchpaths) 798: Com_Printf ("----------\n"); 799: if (s->pack) 800: Com_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); 801: else 802: Com_Printf ("%s\n", s->filename); 803: } 804: 805: Com_Printf ("\nLinks:\n"); 806: for (l=fs_links ; l ; l=l->next) 807: Com_Printf ("%s : %s\n", l->from, l->to); 808: } 809: 810: /* 811: ================ 812: FS_NextPath 813: 814: Allows enumerating all of the directories in the search path 815: ================ 816: */ 817: char *FS_NextPath (char *prevpath) 818: { 819: searchpath_t *s; 820: char *prev; 821: 822: if (!prevpath) 823: return fs_gamedir; 824: 825: prev = fs_gamedir; 826: for (s=fs_searchpaths ; s ; s=s->next) 827: { 828: if (s->pack) 829: continue; 830: if (prevpath == prev) 831: return s->filename; 832: prev = s->filename; 833: } 834: 835: return NULL; 836: } 837: 838: 839: /* 840: ================ 841: FS_InitFilesystem 842: ================ 843: */ 844: void FS_InitFilesystem (void) 845: { 846: Cmd_AddCommand ("path", FS_Path_f); 847: Cmd_AddCommand ("link", FS_Link_f); 848: Cmd_AddCommand ("dir", FS_Dir_f ); 849: 850: // 851: // basedir <path> 852: // allows the game to run from outside the data tree 853: // 854: fs_basedir = Cvar_Get ("basedir", ".", CVAR_NOSET); 855: 856: // 857: // cddir <path> 858: // Logically concatenates the cddir after the basedir for 859: // allows the game to run from outside the data tree 860: // 861: fs_cddir = Cvar_Get ("cddir", "", CVAR_NOSET); 862: if (fs_cddir->string[0]) 863: FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_cddir->string) ); 864: 865: // 866: // start up with baseq2 by default 867: // 868: FS_AddGameDirectory (va("%s/"BASEDIRNAME, fs_basedir->string) ); 869: 870: // any set gamedirs will be freed up to here 871: fs_base_searchpaths = fs_searchpaths; 872: 873: // check for game override 874: fs_gamedirvar = Cvar_Get ("game", "", CVAR_LATCH|CVAR_SERVERINFO); 875: if (fs_gamedirvar->string[0]) 876: FS_SetGamedir (fs_gamedirvar->string); 877: } 878: 879: 880:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.