93
84
should be exactly the same executables as release versions, but with different data that
94
85
automatically restricts where game media can come from to prevent add-ons from working.
96
After the paths are initialized, quake will look for the product.txt file. If not
97
found and verified, the game will run in restricted mode. In restricted mode, only
98
files contained in demoq3/pak0.pk3 will be available for loading, and only if the zip header is
99
verified to not have been modified. A single exception is made for q3config.cfg. Files
100
can still be written out in restricted mode, so screenshots and demos are allowed.
101
Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even
102
if there is a valid product.txt under the basepath or cdpath.
104
If not running in restricted mode, and a file is not found in any local filesystem,
105
an attempt will be made to download it and save it under the base path.
107
If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd
108
path, it will be copied over to the base path. This is a development aid to help build
109
test releases and to copy working sets over slow network links.
111
87
File search order: when FS_FOpenFileRead gets called it will go through the fs_searchpaths
112
88
structure and stop on the first successful hit. fs_searchpaths is built with successive
113
89
calls to FS_AddGameDirectory
700
688
fsh[f].handleFiles.file.o = fopen( ospath, "rb" );
701
689
fsh[f].handleSync = qfalse;
702
if (!fsh[f].handleFiles.file.o)
704
// NOTE TTimo on non *nix systems, fs_homepath == fs_basepath, might want to avoid
705
if (Q_stricmp(fs_homepath->string,fs_basepath->string))
708
ospath = FS_BuildOSPath( fs_basepath->string, filename, "" );
709
ospath[strlen(ospath)-1] = '\0';
711
if ( fs_debug->integer )
713
Com_Printf( "FS_SV_FOpenFileRead (fs_basepath): %s\n", ospath );
716
fsh[f].handleFiles.file.o = fopen( ospath, "rb" );
717
fsh[f].handleSync = qfalse;
719
if ( !fsh[f].handleFiles.file.o )
726
if (!fsh[f].handleFiles.file.o) {
728
ospath = FS_BuildOSPath( fs_cdpath->string, filename, "" );
729
ospath[strlen(ospath)-1] = '\0';
731
if (fs_debug->integer)
733
Com_Printf( "FS_SV_FOpenFileRead (fs_cdpath) : %s\n", ospath );
736
fsh[f].handleFiles.file.o = fopen( ospath, "rb" );
737
fsh[f].handleSync = qfalse;
739
if( !fsh[f].handleFiles.file.o ) {
690
if (!fsh[f].handleFiles.file.o)
692
// If fs_homepath == fs_basepath, don't bother
693
if (Q_stricmp(fs_homepath->string,fs_basepath->string))
696
ospath = FS_BuildOSPath( fs_basepath->string, filename, "" );
697
ospath[strlen(ospath)-1] = '\0';
699
if ( fs_debug->integer )
701
Com_Printf( "FS_SV_FOpenFileRead (fs_basepath): %s\n", ospath );
704
fsh[f].handleFiles.file.o = fopen( ospath, "rb" );
705
fsh[f].handleSync = qfalse;
708
if ( !fsh[f].handleFiles.file.o )
746
716
return FS_filelength(f);
2076
2013
static unsigned int Sys_CountFileList(char **list)
2091
static char** Sys_ConcatenateFileLists( char **list0, char **list1, char **list2 )
2028
static char** Sys_ConcatenateFileLists( char **list0, char **list1 )
2093
int totalLength = 0;
2094
char** cat = NULL, **dst, **src;
2096
totalLength += Sys_CountFileList(list0);
2097
totalLength += Sys_CountFileList(list1);
2098
totalLength += Sys_CountFileList(list2);
2100
/* Create new list. */
2101
dst = cat = Z_Malloc( ( totalLength + 1 ) * sizeof( char* ) );
2103
/* Copy over lists. */
2106
for (src = list0; *src; src++, dst++)
2111
for (src = list1; *src; src++, dst++)
2116
for (src = list2; *src; src++, dst++)
2120
// Terminate the list
2123
// Free our old lists.
2124
// NOTE: not freeing their content, it's been merged in dst and still being used
2125
if (list0) Z_Free( list0 );
2126
if (list1) Z_Free( list1 );
2127
if (list2) Z_Free( list2 );
2030
int totalLength = 0;
2031
char** cat = NULL, **dst, **src;
2033
totalLength += Sys_CountFileList(list0);
2034
totalLength += Sys_CountFileList(list1);
2036
/* Create new list. */
2037
dst = cat = Z_Malloc( ( totalLength + 1 ) * sizeof( char* ) );
2039
/* Copy over lists. */
2042
for (src = list0; *src; src++, dst++)
2047
for (src = list1; *src; src++, dst++)
2051
// Terminate the list
2054
// Free our old lists.
2055
// NOTE: not freeing their content, it's been merged in dst and still being used
2056
if (list0) Z_Free( list0 );
2057
if (list1) Z_Free( list1 );
2139
2069
================
2141
2071
int FS_GetModList( char *listbuf, int bufsize ) {
2142
int nMods, i, j, nTotal, nLen, nPaks, nPotential, nDescLen;
2143
char **pFiles = NULL;
2144
char **pPaks = NULL;
2146
char descPath[MAX_OSPATH];
2147
fileHandle_t descHandle;
2150
char **pFiles0 = NULL;
2151
char **pFiles1 = NULL;
2152
char **pFiles2 = NULL;
2153
qboolean bDrop = qfalse;
2156
nMods = nPotential = nTotal = 0;
2158
pFiles0 = Sys_ListFiles( fs_homepath->string, NULL, NULL, &dummy, qtrue );
2159
pFiles1 = Sys_ListFiles( fs_basepath->string, NULL, NULL, &dummy, qtrue );
2160
pFiles2 = Sys_ListFiles( fs_cdpath->string, NULL, NULL, &dummy, qtrue );
2161
// we searched for mods in the three paths
2162
// it is likely that we have duplicate names now, which we will cleanup below
2163
pFiles = Sys_ConcatenateFileLists( pFiles0, pFiles1, pFiles2 );
2164
nPotential = Sys_CountFileList(pFiles);
2166
for ( i = 0 ; i < nPotential ; i++ ) {
2168
// NOTE: cleaner would involve more changes
2169
// ignore duplicate mod directories
2174
if (Q_stricmp(pFiles[j],name)==0) {
2175
// this one can be dropped
2184
// we drop "baseq3" "." and ".."
2185
if (Q_stricmp(name, BASEGAME) && Q_stricmpn(name, ".", 1)) {
2186
// now we need to find some .pk3 files to validate the mod
2187
// NOTE TTimo: (actually I'm not sure why .. what if it's a mod under developement with no .pk3?)
2188
// we didn't keep the information when we merged the directory names, as to what OS Path it was found under
2189
// so it could be in base path, cd path or home path
2190
// we will try each three of them here (yes, it's a bit messy)
2191
path = FS_BuildOSPath( fs_basepath->string, name, "" );
2193
pPaks = Sys_ListFiles(path, ".pk3", NULL, &nPaks, qfalse);
2194
Sys_FreeFileList( pPaks ); // we only use Sys_ListFiles to check wether .pk3 files are present
2196
/* Try on cd path */
2198
path = FS_BuildOSPath( fs_cdpath->string, name, "" );
2200
pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse );
2201
Sys_FreeFileList( pPaks );
2204
/* try on home path */
2207
path = FS_BuildOSPath( fs_homepath->string, name, "" );
2209
pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse );
2210
Sys_FreeFileList( pPaks );
2214
nLen = strlen(name) + 1;
2215
// nLen is the length of the mod path
2216
// we need to see if there is a description available
2218
strcpy(descPath, name);
2219
strcat(descPath, "/description.txt");
2220
nDescLen = FS_SV_FOpenFileRead( descPath, &descHandle );
2221
if ( nDescLen > 0 && descHandle) {
2223
file = FS_FileForHandle(descHandle);
2224
Com_Memset( descPath, 0, sizeof( descPath ) );
2225
nDescLen = fread(descPath, 1, 48, file);
2226
if (nDescLen >= 0) {
2227
descPath[nDescLen] = '\0';
2229
FS_FCloseFile(descHandle);
2231
strcpy(descPath, name);
2233
nDescLen = strlen(descPath) + 1;
2235
if (nTotal + nLen + 1 + nDescLen + 1 < bufsize) {
2236
strcpy(listbuf, name);
2238
strcpy(listbuf, descPath);
2239
listbuf += nDescLen;
2240
nTotal += nLen + nDescLen;
2249
Sys_FreeFileList( pFiles );
2072
int nMods, i, j, nTotal, nLen, nPaks, nPotential, nDescLen;
2073
char **pFiles = NULL;
2074
char **pPaks = NULL;
2076
char descPath[MAX_OSPATH];
2077
fileHandle_t descHandle;
2080
char **pFiles0 = NULL;
2081
char **pFiles1 = NULL;
2082
qboolean bDrop = qfalse;
2085
nMods = nPotential = nTotal = 0;
2087
pFiles0 = Sys_ListFiles( fs_homepath->string, NULL, NULL, &dummy, qtrue );
2088
pFiles1 = Sys_ListFiles( fs_basepath->string, NULL, NULL, &dummy, qtrue );
2089
// we searched for mods in the three paths
2090
// it is likely that we have duplicate names now, which we will cleanup below
2091
pFiles = Sys_ConcatenateFileLists( pFiles0, pFiles1 );
2092
nPotential = Sys_CountFileList(pFiles);
2094
for ( i = 0 ; i < nPotential ; i++ ) {
2096
// NOTE: cleaner would involve more changes
2097
// ignore duplicate mod directories
2102
if (Q_stricmp(pFiles[j],name)==0) {
2103
// this one can be dropped
2112
// we drop "baseq3" "." and ".."
2113
if (Q_stricmp(name, BASEGAME) && Q_stricmpn(name, ".", 1)) {
2114
// now we need to find some .pk3 files to validate the mod
2115
// NOTE TTimo: (actually I'm not sure why .. what if it's a mod under developement with no .pk3?)
2116
// we didn't keep the information when we merged the directory names, as to what OS Path it was found under
2117
// so it could be in base path, cd path or home path
2118
// we will try each three of them here (yes, it's a bit messy)
2119
path = FS_BuildOSPath( fs_basepath->string, name, "" );
2121
pPaks = Sys_ListFiles(path, ".pk3", NULL, &nPaks, qfalse);
2122
Sys_FreeFileList( pPaks ); // we only use Sys_ListFiles to check wether .pk3 files are present
2124
/* try on home path */
2127
path = FS_BuildOSPath( fs_homepath->string, name, "" );
2129
pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse );
2130
Sys_FreeFileList( pPaks );
2134
nLen = strlen(name) + 1;
2135
// nLen is the length of the mod path
2136
// we need to see if there is a description available
2138
strcpy(descPath, name);
2139
strcat(descPath, "/description.txt");
2140
nDescLen = FS_SV_FOpenFileRead( descPath, &descHandle );
2141
if ( nDescLen > 0 && descHandle) {
2143
file = FS_FileForHandle(descHandle);
2144
Com_Memset( descPath, 0, sizeof( descPath ) );
2145
nDescLen = fread(descPath, 1, 48, file);
2146
if (nDescLen >= 0) {
2147
descPath[nDescLen] = '\0';
2149
FS_FCloseFile(descHandle);
2151
strcpy(descPath, name);
2153
nDescLen = strlen(descPath) + 1;
2155
if (nTotal + nLen + 1 + nDescLen + 1 < bufsize) {
2156
strcpy(listbuf, name);
2158
strcpy(listbuf, descPath);
2159
listbuf += nDescLen;
2160
nTotal += nLen + nDescLen;
2169
Sys_FreeFileList( pFiles );
2650
2555
if ( !havepak && fs_serverReferencedPakNames[i] && *fs_serverReferencedPakNames[i] ) {
2651
2556
// Don't got it
2655
// We need this to make sure we won't hit the end of the buffer or the server could
2656
// overwrite non-pk3 files on clients by writing so much crap into neededpaks that
2657
// Q_strcat cuts off the .pk3 extension.
2659
origpos += strlen(origpos);
2662
Q_strcat( neededpaks, len, "@");
2663
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2664
Q_strcat( neededpaks, len, ".pk3" );
2667
Q_strcat( neededpaks, len, "@");
2668
// Do we have one with the same name?
2669
if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) )
2672
// We already have one called this, we need to download it to another name
2673
// Make something up with the checksum in it
2674
Com_sprintf( st, sizeof( st ), "%s.%08x.pk3", fs_serverReferencedPakNames[i], fs_serverReferencedPaks[i] );
2675
Q_strcat( neededpaks, len, st );
2678
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2679
Q_strcat( neededpaks, len, ".pk3" );
2682
// Find out whether it might have overflowed the buffer and don't add this file to the
2683
// list if that is the case.
2684
if(strlen(origpos) + (origpos - neededpaks) >= len - 1)
2692
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2693
Q_strcat( neededpaks, len, ".pk3" );
2694
// Do we have one with the same name?
2695
if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) )
2697
Q_strcat( neededpaks, len, " (local file exists with wrong checksum)");
2699
Q_strcat( neededpaks, len, "\n");
2560
// We need this to make sure we won't hit the end of the buffer or the server could
2561
// overwrite non-pk3 files on clients by writing so much crap into neededpaks that
2562
// Q_strcat cuts off the .pk3 extension.
2564
origpos += strlen(origpos);
2567
Q_strcat( neededpaks, len, "@");
2568
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2569
Q_strcat( neededpaks, len, ".pk3" );
2572
Q_strcat( neededpaks, len, "@");
2573
// Do we have one with the same name?
2574
if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) )
2577
// We already have one called this, we need to download it to another name
2578
// Make something up with the checksum in it
2579
Com_sprintf( st, sizeof( st ), "%s.%08x.pk3", fs_serverReferencedPakNames[i], fs_serverReferencedPaks[i] );
2580
Q_strcat( neededpaks, len, st );
2584
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2585
Q_strcat( neededpaks, len, ".pk3" );
2588
// Find out whether it might have overflowed the buffer and don't add this file to the
2589
// list if that is the case.
2590
if(strlen(origpos) + (origpos - neededpaks) >= len - 1)
2598
Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] );
2599
Q_strcat( neededpaks, len, ".pk3" );
2600
// Do we have one with the same name?
2601
if ( FS_SV_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) )
2603
Q_strcat( neededpaks, len, " (local file exists with wrong checksum)");
2605
Q_strcat( neededpaks, len, "\n");
2804
2710
================
2806
static void FS_Startup( const char *gameName ) {
2807
const char *homePath;
2712
static void FS_Startup( const char *gameName )
2714
const char *homePath;
2810
2717
Com_Printf( "----- FS_Startup -----\n" );
2812
2719
fs_debug = Cvar_Get( "fs_debug", "0", 0 );
2813
fs_copyfiles = Cvar_Get( "fs_copyfiles", "0", CVAR_INIT );
2814
fs_cdpath = Cvar_Get ("fs_cdpath", Sys_DefaultCDPath(), CVAR_INIT );
2815
2720
fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT );
2816
2721
fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT );
2817
homePath = Sys_DefaultHomePath();
2818
if (!homePath || !homePath[0]) {
2722
homePath = Sys_DefaultHomePath();
2723
if (!homePath || !homePath[0]) {
2819
2724
homePath = fs_basepath->string;
2821
2726
fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT );
2822
2727
fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
2823
fs_restrict = Cvar_Get ("fs_restrict", "", CVAR_INIT );
2825
2729
// add search path elements in reverse priority order
2826
if (fs_cdpath->string[0]) {
2827
FS_AddGameDirectory( fs_cdpath->string, gameName );
2829
2730
if (fs_basepath->string[0]) {
2830
2731
FS_AddGameDirectory( fs_basepath->string, gameName );
2832
// fs_homepath is somewhat particular to *nix systems, only add if relevant
2833
// NOTE: same filtering below for mods and basegame
2834
if (fs_basepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) {
2733
// fs_homepath is somewhat particular to *nix systems, only add if relevant
2736
fs_apppath = Cvar_Get ("fs_apppath", Sys_DefaultAppPath(), CVAR_INIT );
2737
// Make MacOSX also include the base path included with the .app bundle
2738
if (fs_apppath->string[0])
2739
FS_AddGameDirectory(fs_apppath->string, gameName);
2742
// NOTE: same filtering below for mods and basegame
2743
if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string,fs_basepath->string)) {
2835
2744
FS_AddGameDirectory ( fs_homepath->string, gameName );
2838
2747
// check for additional base game so mods can be based upon other mods
2839
2748
if ( fs_basegame->string[0] && !Q_stricmp( gameName, BASEGAME ) && Q_stricmp( fs_basegame->string, gameName ) ) {
2840
if (fs_cdpath->string[0]) {
2841
FS_AddGameDirectory(fs_cdpath->string, fs_basegame->string);
2843
2749
if (fs_basepath->string[0]) {
2844
2750
FS_AddGameDirectory(fs_basepath->string, fs_basegame->string);
2904
2807
static void FS_CheckPak0( void )
2908
qboolean foundPak0 = qfalse;
2910
for( path = fs_searchpaths; path; path = path->next ) {
2912
!Q_stricmpn( path->pack->pakBasename, "pak0", MAX_OSPATH ) &&
2913
(!Q_stricmpn( path->pack->pakGamename, BASEGAME, MAX_OSPATH ) ||
2914
!Q_stricmpn( path->pack->pakGamename, "demoq3", MAX_OSPATH ))) {
2917
if( path->pack->checksum == DEMO_PAK0_CHECKSUM ) {
2809
/*searchpath_t *path;
2810
qboolean founddemo = qfalse;
2811
unsigned foundPak = 0;
2813
for( path = fs_searchpaths; path; path = path->next )
2815
const char* pakBasename = path->pack->pakBasename;
2820
if(!Q_stricmpn( path->pack->pakGamename, "demoq3", MAX_OSPATH )
2821
&& !Q_stricmpn( pakBasename, "pak0", MAX_OSPATH ))
2825
if( path->pack->checksum == DEMO_PAK0_CHECKSUM )
2918
2827
Com_Printf( "\n\n"
2919
2828
"**************************************************\n"
2920
2829
"WARNING: It looks like you're using pak0.pk3\n"
2921
2830
"from the demo. This may work fine, but it is not\n"
2922
2831
"guaranteed or supported.\n"
2923
2832
"**************************************************\n\n\n" );
2924
} else if( path->pack->checksum != PAK0_CHECKSUM ) {
2836
else if(!Q_stricmpn( path->pack->pakGamename, BASEGAME, MAX_OSPATH )
2837
&& strlen(pakBasename) == 4 && !Q_stricmpn( pakBasename, "pak", 3 )
2838
&& pakBasename[3] >= '0' && pakBasename[3] <= '8')
2840
if( path->pack->checksum != pak_checksums[pakBasename[3]-'0'] )
2842
if(pakBasename[0] == '0')
2926
2845
"**************************************************\n"
2927
2846
"WARNING: pak0.pk3 is present but its checksum (%u)\n"
2928
2847
"is not correct. Please re-copy pak0.pk3 from your\n"
2929
2848
"legitimate Q3 CDROM.\n"
2930
2849
"**************************************************\n\n\n",
2931
2850
path->pack->checksum );
2855
"**************************************************\n"
2856
"WARNING: pak%d.pk3 is present but its checksum (%u)\n"
2857
"is not correct. Please re-install the point release\n"
2858
"**************************************************\n\n\n",
2859
pakBasename[3]-'0', path->pack->checksum );
2937
Com_Error( ERR_FATAL, "Couldn't find pak0.pk3. Check that your Q3\n"
2938
"executable is in the correct place and that every file\n"
2939
"in the %s directory is present and readable.", BASEGAME);
2863
foundPak |= 1<<(pakBasename[3]-'0');
2867
if(!founddemo && (foundPak & 0x1ff) != 0x1ff )
2869
if((foundPak&1) != 1 )
2872
"pak0.pk3 is missing. Please copy it\n"
2873
"from your legitimate Q3 CDROM.\n");
2876
if((foundPak&0x1fe) != 0x1fe )
2879
"Point Release files are missing. Please\n"
2880
"re-install the 1.32 point release.\n");
2884
"Also check that your Q3 executable is in\n"
2885
"the correct place and that every file\n"
2886
"in the %s directory is present and readable.\n", BASEGAME);
2888
if(!fs_gamedirvar->string[0]
2889
|| !Q_stricmp( fs_gamedirvar->string, BASEGAME )
2890
|| !Q_stricmp( fs_gamedirvar->string, "missionpack" ))
2891
Com_Error(ERR_FATAL, "You need to install Quake III Arena in order to play");
3304
3255
// we have to specially handle this, because normal command
3305
3256
// line variable sets don't happen until after the filesystem
3306
3257
// has already been initialized
3307
Com_StartupVariable( "fs_cdpath" );
3308
3258
Com_StartupVariable( "fs_basepath" );
3309
3259
Com_StartupVariable( "fs_homepath" );
3310
3260
Com_StartupVariable( "fs_game" );
3311
Com_StartupVariable( "fs_copyfiles" );
3312
Com_StartupVariable( "fs_restrict" );
3314
3262
// try to start up normally
3315
3263
FS_Startup( BASEGAME );
3319
3267
// if we can't find default.cfg, assume that the paths are
3320
3268
// busted and error out now, rather than getting an unreadable
3321
3269
// graphics screen when the font fails to load
3322
3270
if ( FS_ReadFile( "default.cfg", NULL ) <= 0 ) {
3323
3271
Com_Error( ERR_FATAL, "Couldn't load default.cfg" );
3324
// bk001208 - SafeMode see below, FIXME?
3327
3274
Q_strncpyz(lastValidBase, fs_basepath->string, sizeof(lastValidBase));
3328
3275
Q_strncpyz(lastValidGame, fs_gamedirvar->string, sizeof(lastValidGame));
3330
// bk001208 - SafeMode see below, FIXME?