- Small change to search path order - local files not in .pk3s take precedence over files in pk3s. Should make life easier for modders/mappers wanting to override textures that are already contained in some older pk3

- Make VM loading more robust, change loading order: when vm_* == 0 first try loading DLL, then QVM in *each* search directory/path
- Fix FS_FileForHandle that would return a FILE pointer to invalid file handle 0
This commit is contained in:
Thilo Schulz 2011-06-15 22:09:26 +00:00
parent 1ff28b3b2e
commit 9219cde4e8
6 changed files with 584 additions and 368 deletions

View file

@ -215,6 +215,7 @@ typedef struct fileInPack_s {
} fileInPack_t; } fileInPack_t;
typedef struct { typedef struct {
char pakPathname[MAX_OSPATH]; // c:\quake3\baseq3
char pakFilename[MAX_OSPATH]; // c:\quake3\baseq3\pak0.pk3 char pakFilename[MAX_OSPATH]; // c:\quake3\baseq3\pak0.pk3
char pakBasename[MAX_OSPATH]; // pak0 char pakBasename[MAX_OSPATH]; // pak0
char pakGamename[MAX_OSPATH]; // baseq3 char pakGamename[MAX_OSPATH]; // baseq3
@ -230,6 +231,7 @@ typedef struct {
typedef struct { typedef struct {
char path[MAX_OSPATH]; // c:\quake3 char path[MAX_OSPATH]; // c:\quake3
char fullpath[MAX_OSPATH]; // c:\quake3\baseq3
char gamedir[MAX_OSPATH]; // baseq3 char gamedir[MAX_OSPATH]; // baseq3
} directory_t; } directory_t;
@ -393,7 +395,7 @@ static fileHandle_t FS_HandleForFile(void) {
} }
static FILE *FS_FileForHandle( fileHandle_t f ) { static FILE *FS_FileForHandle( fileHandle_t f ) {
if ( f < 0 || f > MAX_FILE_HANDLES ) { if ( f < 1 || f > MAX_FILE_HANDLES ) {
Com_Error( ERR_DROP, "FS_FileForHandle: out of range" ); Com_Error( ERR_DROP, "FS_FileForHandle: out of range" );
} }
if (fsh[f].zipFile == qtrue) { if (fsh[f].zipFile == qtrue) {
@ -413,6 +415,25 @@ void FS_ForceFlush( fileHandle_t f ) {
setvbuf( file, NULL, _IONBF, 0 ); setvbuf( file, NULL, _IONBF, 0 );
} }
/*
================
FS_fplength
================
*/
long FS_fplength(FILE *h)
{
long pos;
long end;
pos = ftell(h);
fseek(h, 0, SEEK_END);
end = ftell(h);
fseek(h, pos, SEEK_SET);
return end;
}
/* /*
================ ================
FS_filelength FS_filelength
@ -422,18 +443,16 @@ it will return the size of the pak file, not the expected
size of the file. size of the file.
================ ================
*/ */
int FS_filelength( fileHandle_t f ) { long FS_filelength(fileHandle_t f)
int pos; {
int end; FILE *h;
FILE* h;
h = FS_FileForHandle(f); h = FS_FileForHandle(f);
pos = ftell (h);
fseek (h, 0, SEEK_END);
end = ftell (h);
fseek (h, pos, SEEK_SET);
return end; if(h == NULL)
return -1;
else
return FS_fplength(h);
} }
/* /*
@ -570,6 +589,28 @@ void FS_HomeRemove( const char *homePath ) {
fs_gamedir, homePath ) ); fs_gamedir, homePath ) );
} }
/*
================
FS_FileInPathExists
Tests if path and file exists
================
*/
qboolean FS_FileInPathExists(const char *testpath)
{
FILE *filep;
filep = fopen(testpath, "rb");
if(filep)
{
fclose(filep);
return qtrue;
}
return qfalse;
}
/* /*
================ ================
FS_FileExists FS_FileExists
@ -580,19 +621,9 @@ search the paths. This is to determine if opening a file to write
NOTE TTimo: this goes with FS_FOpenFileWrite for opening the file afterwards NOTE TTimo: this goes with FS_FOpenFileWrite for opening the file afterwards
================ ================
*/ */
qboolean FS_FileExists( const char *file ) qboolean FS_FileExists(const char *file)
{ {
FILE *f; return FS_FileInPathExists(FS_BuildOSPath(fs_homepath->string, fs_gamedir, file));
char *testpath;
testpath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, file );
f = fopen( testpath, "rb" );
if (f) {
fclose( f );
return qtrue;
}
return qfalse;
} }
/* /*
@ -604,18 +635,12 @@ Tests if the file exists
*/ */
qboolean FS_SV_FileExists( const char *file ) qboolean FS_SV_FileExists( const char *file )
{ {
FILE *f;
char *testpath; char *testpath;
testpath = FS_BuildOSPath( fs_homepath->string, file, ""); testpath = FS_BuildOSPath( fs_homepath->string, file, "");
testpath[strlen(testpath)-1] = '\0'; testpath[strlen(testpath)-1] = '\0';
f = fopen( testpath, "rb" ); return FS_FileInPathExists(testpath);
if (f) {
fclose( f );
return qtrue;
}
return qfalse;
} }
@ -669,7 +694,8 @@ Search for a file somewhere below the home path then base path
in that order in that order
=========== ===========
*/ */
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) { long FS_SV_FOpenFileRead(const char *filename, fileHandle_t *fp)
{
char *ospath; char *ospath;
fileHandle_t f = 0; fileHandle_t f = 0;
@ -1040,6 +1066,256 @@ qboolean FS_IsDemoExt(const char *filename, int namelen)
return qfalse; return qfalse;
} }
/*
===========
FS_FOpenFileReadDir
Tries opening file "filename" in searchpath "search"
Returns filesize and an open FILE pointer.
===========
*/
extern qboolean com_fullyInitialized;
long FS_FOpenFileReadDir(const char *filename, searchpath_t *search, fileHandle_t *file, qboolean uniqueFILE)
{
long hash;
pack_t *pak;
fileInPack_t *pakFile;
directory_t *dir;
char *netpath;
FILE *filep;
int len;
if(filename == NULL)
Com_Error(ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed");
// qpaths are not supposed to have a leading slash
if(filename[0] == '/' || filename[0] == '\\')
filename++;
// make absolutely sure that it can't back up the path.
// The searchpaths do guarantee that something will always
// be prepended, so we don't need to worry about "c:" or "//limbo"
if(strstr(filename, ".." ) || strstr(filename, "::"))
{
if(file == NULL)
return qfalse;
*file = 0;
return -1;
}
// make sure the q3key file is only readable by the quake3.exe at initialization
// any other time the key should only be accessed in memory using the provided functions
if(com_fullyInitialized && strstr(filename, "q3key"))
{
if(file == NULL)
return qfalse;
*file = 0;
return -1;
}
if(file == NULL)
{
// just wants to see if file is there
// is the element a pak file?
if(search->pack)
{
hash = FS_HashFileName(filename, search->pack->hashSize);
if(search->pack->hashTable[hash])
{
// look through all the pak file elements
pak = search->pack;
pakFile = pak->hashTable[hash];
do
{
// case and separator insensitive comparisons
if(!FS_FilenameCompare(pakFile->name, filename))
{
// found it!
if(pakFile->len)
return pakFile->len;
else
{
// It's not nice, but legacy code depends
// on positive value if file exists no matter
// what size
return 1;
}
}
pakFile = pakFile->next;
} while(pakFile != NULL);
}
}
else if(search->dir)
{
dir = search->dir;
netpath = FS_BuildOSPath(dir->path, dir->gamedir, filename);
filep = fopen (netpath, "rb");
if(filep)
{
len = FS_fplength(filep);
fclose(filep);
if(len)
return len;
else
return 1;
}
}
return 0;
}
*file = FS_HandleForFile();
fsh[*file].handleFiles.unique = uniqueFILE;
// is the element a pak file?
if(search->pack)
{
hash = FS_HashFileName(filename, search->pack->hashSize);
if(search->pack->hashTable[hash])
{
// disregard if it doesn't match one of the allowed pure pak files
if(!FS_PakIsPure(search->pack))
{
*file = 0;
return -1;
}
// look through all the pak file elements
pak = search->pack;
pakFile = pak->hashTable[hash];
do
{
// case and separator insensitive comparisons
if(!FS_FilenameCompare(pakFile->name, filename))
{
// found it!
// mark the pak as having been referenced and mark specifics on cgame and ui
// shaders, txt, arena files by themselves do not count as a reference as
// these are loaded from all pk3s
// from every pk3 file..
len = strlen(filename);
if (!(pak->referenced & FS_GENERAL_REF))
{
if(!FS_IsExt(filename, ".shader", len) &&
!FS_IsExt(filename, ".txt", len) &&
!FS_IsExt(filename, ".cfg", len) &&
!FS_IsExt(filename, ".config", len) &&
!FS_IsExt(filename, ".bot", len) &&
!FS_IsExt(filename, ".arena", len) &&
!FS_IsExt(filename, ".menu", len) &&
!strstr(filename, "levelshots"))
{
pak->referenced |= FS_GENERAL_REF;
}
}
if(strstr(filename, "qagame.qvm"))
pak->referenced |= FS_QAGAME_REF;
if(strstr(filename, "cgame.qvm"))
pak->referenced |= FS_CGAME_REF;
if(strstr(filename, "ui.qvm"))
pak->referenced |= FS_UI_REF;
if(uniqueFILE)
{
// open a new file on the pakfile
fsh[*file].handleFiles.file.z = unzOpen(pak->pakFilename);
if(fsh[*file].handleFiles.file.z == NULL)
Com_Error(ERR_FATAL, "Couldn't open %s", pak->pakFilename);
}
else
fsh[*file].handleFiles.file.z = pak->handle;
Q_strncpyz(fsh[*file].name, filename, sizeof(fsh[*file].name));
fsh[*file].zipFile = qtrue;
// set the file position in the zip file (also sets the current file info)
unzSetOffset(fsh[*file].handleFiles.file.z, pakFile->pos);
// open the file in the zip
unzOpenCurrentFile(fsh[*file].handleFiles.file.z);
fsh[*file].zipFilePos = pakFile->pos;
if(fs_debug->integer)
{
Com_Printf("FS_FOpenFileRead: %s (found in '%s')\n",
filename, pak->pakFilename);
}
return pakFile->len;
}
pakFile = pakFile->next;
} while(pakFile != NULL);
}
}
else if(search->dir)
{
// check a file in the directory tree
// if we are running restricted, the only files we
// will allow to come from the directory are .cfg files
len = strlen(filename);
// FIXME TTimo I'm not sure about the fs_numServerPaks test
// if you are using FS_ReadFile to find out if a file exists,
// this test can make the search fail although the file is in the directory
// I had the problem on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8
// turned out I used FS_FileExists instead
if(fs_numServerPaks)
{
if(!FS_IsExt(filename, ".cfg", len) && // for config files
!FS_IsExt(filename, ".menu", len) && // menu files
!FS_IsExt(filename, ".game", len) && // menu files
!FS_IsExt(filename, ".cfg", len) && // for journal files
!FS_IsDemoExt(filename, len)) // demos
{
*file = 0;
return -1;
}
}
dir = search->dir;
netpath = FS_BuildOSPath(dir->path, dir->gamedir, filename);
filep = fopen(netpath, "rb");
if (filep == NULL)
{
*file = 0;
return -1;
}
Q_strncpyz(fsh[*file].name, filename, sizeof(fsh[*file].name));
fsh[*file].zipFile = qfalse;
if(fs_debug->integer)
{
Com_Printf("FS_FOpenFileRead: %s (found in '%s/%s')\n", filename,
dir->path, dir->gamedir);
}
fsh[*file].handleFiles.file.o = filep;
return FS_fplength(filep);
}
return -1;
}
/* /*
=========== ===========
FS_FOpenFileRead FS_FOpenFileRead
@ -1050,242 +1326,134 @@ Used for streaming data out of either a
separate file or a ZIP file. separate file or a ZIP file.
=========== ===========
*/ */
extern qboolean com_fullyInitialized; long FS_FOpenFileRead(const char *filename, fileHandle_t *file, qboolean uniqueFILE)
{
int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE ) {
searchpath_t *search; searchpath_t *search;
char *netpath; long len;
pack_t *pak;
fileInPack_t *pakFile;
directory_t *dir;
long hash;
FILE *temp;
int l;
hash = 0; if(!fs_searchpaths)
Com_Error(ERR_FATAL, "Filesystem call made without initialization");
if ( !fs_searchpaths ) { for(search = fs_searchpaths; search; search = search->next)
Com_Error( ERR_FATAL, "Filesystem call made without initialization" );
}
if ( file == NULL ) {
// just wants to see if file is there
for ( search = fs_searchpaths ; search ; search = search->next ) {
//
if ( search->pack ) {
hash = FS_HashFileName(filename, search->pack->hashSize);
}
// is the element a pak file?
if ( search->pack && search->pack->hashTable[hash] ) {
// look through all the pak file elements
pak = search->pack;
pakFile = pak->hashTable[hash];
do {
// case and separator insensitive comparisons
if ( !FS_FilenameCompare( pakFile->name, filename ) ) {
// found it!
return qtrue;
}
pakFile = pakFile->next;
} while(pakFile != NULL);
} else if ( search->dir ) {
dir = search->dir;
netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename );
temp = fopen (netpath, "rb");
if ( !temp ) {
continue;
}
fclose(temp);
return qtrue;
}
}
return qfalse;
}
if ( !filename ) {
Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed" );
}
// qpaths are not supposed to have a leading slash
if ( filename[0] == '/' || filename[0] == '\\' ) {
filename++;
}
// make absolutely sure that it can't back up the path.
// The searchpaths do guarantee that something will always
// be prepended, so we don't need to worry about "c:" or "//limbo"
if ( strstr( filename, ".." ) || strstr( filename, "::" ) ) {
*file = 0;
return -1;
}
// make sure the q3key file is only readable by the quake3.exe at initialization
// any other time the key should only be accessed in memory using the provided functions
if( com_fullyInitialized && strstr( filename, "q3key" ) ) {
*file = 0;
return -1;
}
//
// search through the path, one element at a time
//
*file = FS_HandleForFile();
fsh[*file].handleFiles.unique = uniqueFILE;
for ( search = fs_searchpaths ; search ; search = search->next ) {
//
if ( search->pack ) {
hash = FS_HashFileName(filename, search->pack->hashSize);
}
// is the element a pak file?
if ( search->pack && search->pack->hashTable[hash] ) {
// disregard if it doesn't match one of the allowed pure pak files
if ( !FS_PakIsPure(search->pack) ) {
continue;
}
// look through all the pak file elements
pak = search->pack;
pakFile = pak->hashTable[hash];
do {
// case and separator insensitive comparisons
if ( !FS_FilenameCompare( pakFile->name, filename ) ) {
// found it!
// mark the pak as having been referenced and mark specifics on cgame and ui
// shaders, txt, arena files by themselves do not count as a reference as
// these are loaded from all pk3s
// from every pk3 file..
l = strlen(filename);
if (!(pak->referenced & FS_GENERAL_REF))
{ {
if(!FS_IsExt(filename, ".shader", l) && len = FS_FOpenFileReadDir(filename, search, file, uniqueFILE);
!FS_IsExt(filename, ".txt", l) &&
!FS_IsExt(filename, ".cfg", l) && if(file == NULL)
!FS_IsExt(filename, ".config", l) &&
!FS_IsExt(filename, ".bot", l) &&
!FS_IsExt(filename, ".arena", l) &&
!FS_IsExt(filename, ".menu", l) &&
!strstr(filename, "levelshots"))
{ {
pak->referenced |= FS_GENERAL_REF; if(len > 0)
return len;
} }
} else
if (!(pak->referenced & FS_QAGAME_REF) && strstr(filename, "qagame.qvm")) {
pak->referenced |= FS_QAGAME_REF;
}
if (!(pak->referenced & FS_CGAME_REF) && strstr(filename, "cgame.qvm")) {
pak->referenced |= FS_CGAME_REF;
}
if (!(pak->referenced & FS_UI_REF) && strstr(filename, "ui.qvm")) {
pak->referenced |= FS_UI_REF;
}
if ( uniqueFILE ) {
// open a new file on the pakfile
fsh[*file].handleFiles.file.z = unzOpen (pak->pakFilename);
if (fsh[*file].handleFiles.file.z == NULL) {
Com_Error (ERR_FATAL, "Couldn't open %s", pak->pakFilename);
}
} else {
fsh[*file].handleFiles.file.z = pak->handle;
}
Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) );
fsh[*file].zipFile = qtrue;
// set the file position in the zip file (also sets the current file info)
unzSetOffset(fsh[*file].handleFiles.file.z, pakFile->pos);
// open the file in the zip
unzOpenCurrentFile( fsh[*file].handleFiles.file.z );
fsh[*file].zipFilePos = pakFile->pos;
if ( fs_debug->integer ) {
Com_Printf( "FS_FOpenFileRead: %s (found in '%s')\n",
filename, pak->pakFilename );
}
return pakFile->len;
}
pakFile = pakFile->next;
} while(pakFile != NULL);
} else if ( search->dir ) {
// check a file in the directory tree
// if we are running restricted, the only files we
// will allow to come from the directory are .cfg files
l = strlen( filename );
// FIXME TTimo I'm not sure about the fs_numServerPaks test
// if you are using FS_ReadFile to find out if a file exists,
// this test can make the search fail although the file is in the directory
// I had the problem on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8
// turned out I used FS_FileExists instead
if(fs_numServerPaks)
{ {
if(!FS_IsExt(filename, ".cfg", l) && // for config files if(len >= 0 && *file)
!FS_IsExt(filename, ".menu", l) && // menu files return len;
!FS_IsExt(filename, ".game", l) && // menu files
!FS_IsExt(filename, ".cfg", l) && // for journal files
!FS_IsDemoExt(filename, l)) // demos
{
continue;
}
} }
dir = search->dir;
netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename );
fsh[*file].handleFiles.file.o = fopen (netpath, "rb");
if ( !fsh[*file].handleFiles.file.o ) {
continue;
}
Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) );
fsh[*file].zipFile = qfalse;
if ( fs_debug->integer ) {
Com_Printf( "FS_FOpenFileRead: %s (found in '%s/%s')\n", filename,
dir->path, dir->gamedir );
}
return FS_filelength (*file);
}
} }
#ifdef FS_MISSING #ifdef FS_MISSING
if (missingFiles) { if(missingFiles)
fprintf(missingFiles, "%s\n", filename); fprintf(missingFiles, "%s\n", filename);
}
#endif #endif
if(file)
*file = 0; *file = 0;
return -1; return -1;
} }
/*
=================
FS_FindVM
char *FS_FindDll( const char *filename ) { Find a suitable VM file in search path order.
searchpath_t *search;
In each searchpath try:
- open DLL file if DLL loading enabled
- open QVM file
Enable search for DLL by setting enableDll to FSVM_ENABLEDLL
write found DLL or QVM to "found" and return VMI_NATIVE if DLL, VMI_COMPILED if QVM
Return the searchpath in "startSearch".
=================
*/
vmInterpret_t FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, int enableDll)
{
searchpath_t *search, *lastSearch;
directory_t *dir; directory_t *dir;
pack_t *pack;
if ( !fs_searchpaths ) { char dllName[MAX_OSPATH], qvmName[MAX_OSPATH];
Com_Error( ERR_FATAL, "Filesystem call made without initialization" );
}
for ( search = fs_searchpaths ; search ; search = search->next ) {
if ( search->dir ) {
FILE *f;
char *netpath; char *netpath;
if(!fs_searchpaths)
Com_Error(ERR_FATAL, "Filesystem call made without initialization");
if(enableDll)
Com_sprintf(dllName, sizeof(dllName), "%s" ARCH_STRING DLL_EXT, name);
Com_sprintf(qvmName, sizeof(dllName), "vm/%s.qvm", name);
lastSearch = *startSearch;
if(*startSearch == NULL)
search = fs_searchpaths;
else
search = lastSearch->next;
while(search)
{
if(search->dir)
{
dir = search->dir; dir = search->dir;
netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename );
f = fopen( netpath, "rb" ); if(enableDll)
if (f) { {
fclose( f ); netpath = FS_BuildOSPath(dir->path, dir->gamedir, dllName);
return netpath;
} if(FS_FileInPathExists(netpath))
{
Q_strncpyz(found, netpath, foundlen);
*startSearch = search;
return VMI_NATIVE;
} }
} }
return NULL; if(FS_FOpenFileReadDir(qvmName, search, NULL, qfalse) > 0)
{
*startSearch = search;
return VMI_COMPILED;
}
}
else if(search->pack)
{
pack = search->pack;
if(lastSearch && lastSearch->pack)
{
// make sure we only try loading one VM file per game dir
// i.e. if VM from pak7.pk3 fails we won't try one from pak6.pk3
if(!FS_FilenameCompare(lastSearch->pack->pakPathname, pack->pakPathname))
{
search = search->next;
continue;
}
}
if(FS_FOpenFileReadDir(qvmName, search, NULL, qfalse) > 0)
{
*startSearch = search;
return VMI_COMPILED;
}
}
search = search->next;
}
return -1;
} }
/* /*
@ -1572,17 +1740,20 @@ int FS_FileIsInPAK(const char *filename, int *pChecksum ) {
/* /*
============ ============
FS_ReadFile FS_ReadFileDir
Filename are relative to the quake search path Filename are relative to the quake search path
a null buffer will just return the file length without loading a null buffer will just return the file length without loading
If searchPath is non-NULL search only in that specific search path
============ ============
*/ */
int FS_ReadFile( const char *qpath, void **buffer ) { long FS_ReadFileDir(const char *qpath, void *searchPath, void **buffer)
{
fileHandle_t h; fileHandle_t h;
searchpath_t *search;
byte* buf; byte* buf;
qboolean isConfig; qboolean isConfig;
int len; long len;
if ( !fs_searchpaths ) { if ( !fs_searchpaths ) {
Com_Error( ERR_FATAL, "Filesystem call made without initialization" ); Com_Error( ERR_FATAL, "Filesystem call made without initialization" );
@ -1639,8 +1810,19 @@ int FS_ReadFile( const char *qpath, void **buffer ) {
isConfig = qfalse; isConfig = qfalse;
} }
search = searchPath;
if(search == NULL)
{
// look for it in the filesystem or pack files // look for it in the filesystem or pack files
len = FS_FOpenFileRead( qpath, &h, qfalse ); len = FS_FOpenFileRead(qpath, &h, qfalse);
}
else
{
// look for it in a specific search path only
len = FS_FOpenFileReadDir(qpath, search, &h, qfalse);
}
if ( h == 0 ) { if ( h == 0 ) {
if ( buffer ) { if ( buffer ) {
*buffer = NULL; *buffer = NULL;
@ -1687,6 +1869,19 @@ int FS_ReadFile( const char *qpath, void **buffer ) {
return len; return len;
} }
/*
============
FS_ReadFile
Filename are relative to the quake search path
a null buffer will just return the file length without loading
============
*/
long FS_ReadFile(const char *qpath, void **buffer)
{
return FS_ReadFileDir(qpath, NULL, buffer);
}
/* /*
============= =============
FS_FreeFile FS_FreeFile
@ -2541,6 +2736,33 @@ void FS_TouchFile_f( void ) {
} }
} }
/*
============
FS_Which
============
*/
qboolean FS_Which(const char *filename, void *searchPath)
{
searchpath_t *search = searchPath;
if(FS_FOpenFileReadDir(filename, search, NULL, qfalse) > 0)
{
if(search->pack)
{
Com_Printf("File \"%s\" found in \"%s\"\n", filename, search->pack->pakFilename);
return qtrue;
}
else if(search->dir)
{
Com_Printf( "File \"%s\" found at \"%s\"\n", filename, search->dir->fullpath);
return qtrue;
}
}
return qfalse;
}
/* /*
============ ============
FS_Which_f FS_Which_f
@ -2548,16 +2770,8 @@ FS_Which_f
*/ */
void FS_Which_f( void ) { void FS_Which_f( void ) {
searchpath_t *search; searchpath_t *search;
char *netpath;
pack_t *pak;
fileInPack_t *pakFile;
directory_t *dir;
long hash;
FILE *temp;
char *filename; char *filename;
char buf[ MAX_OSPATH ];
hash = 0;
filename = Cmd_Argv(1); filename = Cmd_Argv(1);
if ( !filename[0] ) { if ( !filename[0] ) {
@ -2571,40 +2785,13 @@ void FS_Which_f( void ) {
} }
// just wants to see if file is there // just wants to see if file is there
for ( search = fs_searchpaths ; search ; search = search->next ) { for(search = fs_searchpaths; search; search = search->next)
if ( search->pack ) { {
hash = FS_HashFileName(filename, search->pack->hashSize); if(FS_Which(filename, search))
}
// is the element a pak file?
if ( search->pack && search->pack->hashTable[hash] ) {
// look through all the pak file elements
pak = search->pack;
pakFile = pak->hashTable[hash];
do {
// case and separator insensitive comparisons
if ( !FS_FilenameCompare( pakFile->name, filename ) ) {
// found it!
Com_Printf( "File \"%s\" found in \"%s\"\n", filename, pak->pakFilename );
return; return;
} }
pakFile = pakFile->next;
} while(pakFile != NULL);
} else if ( search->dir ) {
dir = search->dir;
netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); Com_Printf("File not found: \"%s\"\n", filename);
temp = fopen (netpath, "rb");
if ( !temp ) {
continue;
}
fclose(temp);
Com_sprintf( buf, sizeof( buf ), "%s/%s", dir->path, dir->gamedir );
FS_ReplaceSeparators( buf );
Com_Printf( "File \"%s\" found at \"%s\"\n", filename, buf );
return;
}
}
Com_Printf( "File not found: \"%s\"\n", filename );
return; return;
} }
@ -2634,7 +2821,7 @@ void FS_AddGameDirectory( const char *path, const char *dir ) {
int i; int i;
searchpath_t *search; searchpath_t *search;
pack_t *pak; pack_t *pak;
char *pakfile; char curpath[MAX_OSPATH + 1], *pakfile;
int numfiles; int numfiles;
char **pakfiles; char **pakfiles;
@ -2647,22 +2834,11 @@ void FS_AddGameDirectory( const char *path, const char *dir ) {
Q_strncpyz( fs_gamedir, dir, sizeof( fs_gamedir ) ); Q_strncpyz( fs_gamedir, dir, sizeof( fs_gamedir ) );
//
// add the directory to the search path
//
search = Z_Malloc (sizeof(searchpath_t));
search->dir = Z_Malloc( sizeof( *search->dir ) );
Q_strncpyz( search->dir->path, path, sizeof( search->dir->path ) );
Q_strncpyz( search->dir->gamedir, dir, sizeof( search->dir->gamedir ) );
search->next = fs_searchpaths;
fs_searchpaths = search;
// find all pak files in this directory // find all pak files in this directory
pakfile = FS_BuildOSPath( path, dir, "" ); Q_strncpyz(curpath, FS_BuildOSPath(path, dir, ""), sizeof(curpath));
pakfile[ strlen(pakfile) - 1 ] = 0; // strip the trailing slash curpath[strlen(curpath) - 1] = '\0'; // strip the trailing slash
pakfiles = Sys_ListFiles( pakfile, ".pk3", NULL, &numfiles, qfalse ); pakfiles = Sys_ListFiles(curpath, ".pk3", NULL, &numfiles, qfalse);
qsort( pakfiles, numfiles, sizeof(char*), paksort ); qsort( pakfiles, numfiles, sizeof(char*), paksort );
@ -2670,8 +2846,10 @@ void FS_AddGameDirectory( const char *path, const char *dir ) {
pakfile = FS_BuildOSPath( path, dir, pakfiles[i] ); pakfile = FS_BuildOSPath( path, dir, pakfiles[i] );
if ( ( pak = FS_LoadZipFile( pakfile, pakfiles[i] ) ) == 0 ) if ( ( pak = FS_LoadZipFile( pakfile, pakfiles[i] ) ) == 0 )
continue; continue;
Q_strncpyz(pak->pakPathname, curpath, sizeof(pak->pakPathname));
// store the game name for downloading // store the game name for downloading
strcpy(pak->pakGamename, dir); Q_strncpyz(pak->pakGamename, dir, sizeof(pak->pakGamename));
fs_packFiles += pak->numfiles; fs_packFiles += pak->numfiles;
@ -2683,6 +2861,19 @@ void FS_AddGameDirectory( const char *path, const char *dir ) {
// done // done
Sys_FreeFileList( pakfiles ); Sys_FreeFileList( pakfiles );
//
// add the directory to the search path
//
search = Z_Malloc (sizeof(searchpath_t));
search->dir = Z_Malloc( sizeof( *search->dir ) );
Q_strncpyz(search->dir->path, path, sizeof(search->dir->path));
Q_strncpyz(search->dir->fullpath, curpath, sizeof(search->dir->fullpath));
Q_strncpyz(search->dir->gamedir, dir, sizeof(search->dir->gamedir));
search->next = fs_searchpaths;
fs_searchpaths = search;
} }
/* /*

View file

@ -614,7 +614,7 @@ qboolean FS_FileExists( const char *file );
qboolean FS_CreatePath (char *OSPath); qboolean FS_CreatePath (char *OSPath);
char *FS_FindDll( const char *filename ); vmInterpret_t FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, int enableDll);
char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); char *FS_BuildOSPath( const char *base, const char *game, const char *qpath );
qboolean FS_CompareZipChecksum(const char *zipfile); qboolean FS_CompareZipChecksum(const char *zipfile);
@ -630,9 +630,9 @@ fileHandle_t FS_FCreateOpenPipeFile( const char *filename );
// will properly create any needed paths and deal with seperater character issues // will properly create any needed paths and deal with seperater character issues
fileHandle_t FS_SV_FOpenFileWrite( const char *filename ); fileHandle_t FS_SV_FOpenFileWrite( const char *filename );
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ); long FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp );
void FS_SV_Rename( const char *from, const char *to ); void FS_SV_Rename( const char *from, const char *to );
int FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE ); long FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE );
// if uniqueFILE is true, then a new FILE will be fopened even if the file // if uniqueFILE is true, then a new FILE will be fopened even if the file
// is found in an already open pak file. If uniqueFILE is false, you must call // is found in an already open pak file. If uniqueFILE is false, you must call
// FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed // FS_FCloseFile instead of fclose, otherwise the pak FILE would be improperly closed
@ -651,7 +651,8 @@ int FS_Read( void *buffer, int len, fileHandle_t f );
void FS_FCloseFile( fileHandle_t f ); void FS_FCloseFile( fileHandle_t f );
// note: you can't just fclose from another DLL, due to MS libc issues // note: you can't just fclose from another DLL, due to MS libc issues
int FS_ReadFile( const char *qpath, void **buffer ); long FS_ReadFileDir(const char *qpath, void *searchPath, void **buffer);
long FS_ReadFile(const char *qpath, void **buffer);
// returns the length of the file // returns the length of the file
// a null buffer will just return the file length without loading // a null buffer will just return the file length without loading
// as a quick check for existance. -1 length == not present // as a quick check for existance. -1 length == not present
@ -668,7 +669,7 @@ void FS_FreeFile( void *buffer );
void FS_WriteFile( const char *qpath, const void *buffer, int size ); void FS_WriteFile( const char *qpath, const void *buffer, int size );
// writes a complete file, creating any subdirectories needed // writes a complete file, creating any subdirectories needed
int FS_filelength( fileHandle_t f ); long FS_filelength(fileHandle_t f);
// doesn't work for files that are opened from a pack file // doesn't work for files that are opened from a pack file
int FS_FTell( fileHandle_t f ); int FS_FTell( fileHandle_t f );
@ -726,6 +727,7 @@ void FS_FilenameCompletion( const char *dir, const char *ext,
qboolean stripExt, void(*callback)(const char *s), qboolean allowNonPureFilesOnDisk ); qboolean stripExt, void(*callback)(const char *s), qboolean allowNonPureFilesOnDisk );
const char *FS_GetCurrentGameDir(void); const char *FS_GetCurrentGameDir(void);
qboolean FS_Which(const char *filename, void *searchPath);
/* /*
============================================================== ==============================================================

View file

@ -377,15 +377,20 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
// load the image // load the image
Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
Com_Printf( "Loading vm file %s...\n", filename ); Com_Printf( "Loading vm file %s...\n", filename );
length = FS_ReadFile( filename, &header.v );
length = FS_ReadFileDir(filename, vm->searchPath, &header.v);
if ( !header.h ) { if ( !header.h ) {
Com_Printf( "Failed.\n" ); Com_Printf( "Failed.\n" );
VM_Free( vm ); VM_Free( vm );
Com_Printf(S_COLOR_YELLOW "Warning: Couldn't open VM file %s\n", filename);
return NULL; return NULL;
} }
// show where the qvm was loaded from // show where the qvm was loaded from
Cmd_ExecuteString( va( "which %s\n", filename ) ); FS_Which(filename, vm->searchPath);
if( LittleLong( header.h->vmMagic ) == VM_MAGIC_VER2 ) { if( LittleLong( header.h->vmMagic ) == VM_MAGIC_VER2 ) {
Com_Printf( "...which has vmMagic VM_MAGIC_VER2\n" ); Com_Printf( "...which has vmMagic VM_MAGIC_VER2\n" );
@ -400,9 +405,13 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
|| header.h->bssLength < 0 || header.h->bssLength < 0
|| header.h->dataLength < 0 || header.h->dataLength < 0
|| header.h->litLength < 0 || header.h->litLength < 0
|| header.h->codeLength <= 0 ) { || header.h->codeLength <= 0 )
VM_Free( vm ); {
Com_Error( ERR_FATAL, "%s has bad header", filename ); VM_Free(vm);
FS_FreeFile(header.v);
Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename);
return NULL;
} }
} else if( LittleLong( header.h->vmMagic ) == VM_MAGIC ) { } else if( LittleLong( header.h->vmMagic ) == VM_MAGIC ) {
// byte swap the header // byte swap the header
@ -415,14 +424,21 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
if ( header.h->bssLength < 0 if ( header.h->bssLength < 0
|| header.h->dataLength < 0 || header.h->dataLength < 0
|| header.h->litLength < 0 || header.h->litLength < 0
|| header.h->codeLength <= 0 ) { || header.h->codeLength <= 0 )
VM_Free( vm ); {
Com_Error( ERR_FATAL, "%s has bad header", filename ); VM_Free(vm);
FS_FreeFile(header.v);
Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename);
return NULL;
} }
} else { } else {
VM_Free( vm ); VM_Free( vm );
Com_Error( ERR_FATAL, "%s does not have a recognisable " FS_FreeFile(header.v);
"magic number in its header", filename );
Com_Printf(S_COLOR_YELLOW "Warning: %s does not have a recognisable "
"magic number in its header\n", filename);
return NULL;
} }
// round up to next power of 2 so all data operations can // round up to next power of 2 so all data operations can
@ -524,7 +540,9 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *),
vmInterpret_t interpret ) { vmInterpret_t interpret ) {
vm_t *vm; vm_t *vm;
vmHeader_t *header; vmHeader_t *header;
int i, remaining; int i, remaining, retval;
char filename[MAX_OSPATH];
void *startSearch = NULL;
if ( !module || !module[0] || !systemCalls ) { if ( !module || !module[0] || !systemCalls ) {
Com_Error( ERR_FATAL, "VM_Create: bad parms" ); Com_Error( ERR_FATAL, "VM_Create: bad parms" );
@ -553,25 +571,41 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *),
vm = &vmTable[i]; vm = &vmTable[i];
Q_strncpyz( vm->name, module, sizeof( vm->name ) ); Q_strncpyz(vm->name, module, sizeof(vm->name));
vm->systemCall = systemCalls;
if ( interpret == VMI_NATIVE ) { do
// try to load as a system dll {
Com_Printf( "Loading dll file %s.\n", vm->name ); retval = FS_FindVM(&startSearch, filename, sizeof(filename), module, (interpret == VMI_NATIVE));
vm->dllHandle = Sys_LoadDll( module, &vm->entryPoint, VM_DllSyscall );
if ( vm->dllHandle ) { if(retval == VMI_NATIVE)
{
Com_Printf("Try loading dll file %s\n", filename);
vm->dllHandle = Sys_LoadDll(filename, &vm->entryPoint, VM_DllSyscall);
if(vm->dllHandle)
{
vm->systemCall = systemCalls;
return vm; return vm;
} }
Com_Printf( "Failed to load dll, looking for qvm.\n" ); Com_Printf("Failed loading dll, trying next\n");
interpret = VMI_COMPILED;
} }
else if(retval == VMI_COMPILED)
{
vm->searchPath = startSearch;
if((header = VM_LoadQVM(vm, qtrue)))
break;
// load the image // VM_Free overwrites the name on failed load
if( !( header = VM_LoadQVM( vm, qtrue ) ) ) { Q_strncpyz(vm->name, module, sizeof(vm->name));
return NULL;
} }
} while(retval >= 0);
if(retval < 0)
return NULL;
vm->systemCall = systemCalls;
// allocate space for the jump targets, which will be filled in by the compile/prep functions // allocate space for the jump targets, which will be filled in by the compile/prep functions
vm->instructionCount = header->instructionCount; vm->instructionCount = header->instructionCount;

View file

@ -142,6 +142,7 @@ struct vm_s {
//------------------------------------ //------------------------------------
char name[MAX_QPATH]; char name[MAX_QPATH];
void *searchPath; // hint for FS_ReadFileDir()
// for dynamic linked modules // for dynamic linked modules
void *dllHandle; void *dllHandle;

View file

@ -147,7 +147,7 @@ typedef struct {
// a -1 return means the file does not exist // a -1 return means the file does not exist
// NULL can be passed for buf to just determine existance // NULL can be passed for buf to just determine existance
int (*FS_FileIsInPAK)( const char *name, int *pCheckSum ); int (*FS_FileIsInPAK)( const char *name, int *pCheckSum );
int (*FS_ReadFile)( const char *name, void **buf ); long (*FS_ReadFile)( const char *name, void **buf );
void (*FS_FreeFile)( void *buf ); void (*FS_FreeFile)( void *buf );
char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound ); char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound );
void (*FS_FreeFileList)( char **filelist ); void (*FS_FreeFileList)( char **filelist );

View file

@ -412,35 +412,23 @@ void Sys_UnloadDll( void *dllHandle )
Sys_LoadDll Sys_LoadDll
Used to load a development dll instead of a virtual machine Used to load a development dll instead of a virtual machine
#1 look in fs_homepath
#2 look in fs_basepath
================= =================
*/ */
void * QDECL Sys_LoadDll( const char *name, void *Sys_LoadDll(const char *name,
intptr_t (QDECL **entryPoint)(int, ...), intptr_t (QDECL **entryPoint)(int, ...),
intptr_t (*systemcalls)(intptr_t, ...) ) intptr_t (*systemcalls)(intptr_t, ...))
{ {
void *libHandle; void *libHandle;
void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ); void (*dllEntry)(intptr_t (*syscallptr)(intptr_t, ...));
char fname[MAX_OSPATH];
char *netpath;
assert( name ); assert(name);
Com_sprintf(fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name); Com_Printf( "Loading DLL file: %s\n", name);
libHandle = Sys_LoadLibrary(name);
netpath = FS_FindDll(fname); if(!libHandle)
{
if(!netpath) { Com_Printf("Sys_LoadDll(%s) failed:\n\"%s\"\n", name, Sys_LibraryError());
Com_Printf( "Sys_LoadDll(%s) could not find it\n", fname );
return NULL;
}
Com_Printf( "Loading DLL file: %s\n", netpath);
libHandle = Sys_LoadLibrary(netpath);
if(!libHandle) {
Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", netpath, Sys_LibraryError() );
return NULL; return NULL;
} }