diff options
author | Thilo Schulz <arny@ats.s.bawue.de> | 2011-06-15 22:09:26 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-01-10 21:28:20 +0000 |
commit | fad34584b3019a9c46b4b2255e6428ffc3aa04b3 (patch) | |
tree | 6b68be8be7b66c6671cbba3928145794b420c7e8 /src | |
parent | 7de3b3918c13d34da9a8a468ee7bbe4c70504e87 (diff) |
- 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
Diffstat (limited to 'src')
-rw-r--r-- | src/qcommon/files.c | 671 | ||||
-rw-r--r-- | src/qcommon/qcommon.h | 12 | ||||
-rw-r--r-- | src/qcommon/vm.c | 88 | ||||
-rw-r--r-- | src/qcommon/vm_local.h | 3 | ||||
-rw-r--r-- | src/renderer/tr_public.h | 2 | ||||
-rw-r--r-- | src/sys/sys_main.c | 32 |
6 files changed, 510 insertions, 298 deletions
diff --git a/src/qcommon/files.c b/src/qcommon/files.c index 76cc6075..66cef9a3 100644 --- a/src/qcommon/files.c +++ b/src/qcommon/files.c @@ -193,6 +193,7 @@ typedef struct fileInPack_s { } fileInPack_t; typedef struct { + char pakPathname[MAX_OSPATH]; // /tremulous/baseq3 char pakFilename[MAX_OSPATH]; // /tremulous/base/pak0.pk3 char pakBasename[MAX_OSPATH]; // pak0 char pakGamename[MAX_OSPATH]; // base @@ -208,6 +209,7 @@ typedef struct { typedef struct { char path[MAX_OSPATH]; + char fullpath[MAX_OSPATH]; // /tremulous/base char gamedir[MAX_OSPATH]; // base } directory_t; @@ -371,7 +373,7 @@ static fileHandle_t FS_HandleForFile(void) { } 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" ); } if (fsh[f].zipFile == qtrue) { @@ -393,6 +395,25 @@ void FS_ForceFlush( fileHandle_t f ) { /* ================ +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 If this is called on a non-unique FILE (from a pak file), @@ -400,18 +421,16 @@ it will return the size of the pak file, not the expected size of the file. ================ */ -int FS_filelength( fileHandle_t f ) { - int pos; - int end; - FILE* h; +long FS_filelength(fileHandle_t f) +{ + FILE *h; 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); } /* @@ -550,6 +569,28 @@ void FS_HomeRemove( const char *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 Tests if the file exists in the current gamedir, this DOES NOT @@ -558,19 +599,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 ================ */ -qboolean FS_FileExists( const char *file ) +qboolean FS_FileExists(const char *file) { - FILE *f; - char *testpath; - - testpath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, file ); - - f = fopen( testpath, "rb" ); - if (f) { - fclose( f ); - return qtrue; - } - return qfalse; + return FS_FileInPathExists(FS_BuildOSPath(fs_homepath->string, fs_gamedir, file)); } /* @@ -582,18 +613,12 @@ Tests if the file exists */ qboolean FS_SV_FileExists( const char *file ) { - FILE *f; char *testpath; testpath = FS_BuildOSPath( fs_homepath->string, file, ""); testpath[strlen(testpath)-1] = '\0'; - f = fopen( testpath, "rb" ); - if (f) { - fclose( f ); - return qtrue; - } - return qfalse; + return FS_FileInPathExists(testpath); } @@ -647,7 +672,8 @@ Search for a file somewhere below the home path then base path 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; fileHandle_t f = 0; @@ -1020,243 +1046,381 @@ qboolean FS_IsDemoExt(const char *filename, int namelen) /* =========== -FS_FOpenFileRead +FS_FOpenFileReadDir -Finds the file in the search path. +Tries opening file "filename" in searchpath "search" Returns filesize and an open FILE pointer. -Used for streaming data out of either a -separate file or a ZIP file. =========== */ extern qboolean com_fullyInitialized; -int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE ) { - searchpath_t *search; - char *netpath; - pack_t *pak; - fileInPack_t *pakFile; - directory_t *dir; +long FS_FOpenFileReadDir(const char *filename, searchpath_t *search, fileHandle_t *file, qboolean uniqueFILE) +{ long hash; - FILE *temp; - int l; + pack_t *pak; + fileInPack_t *pakFile; + directory_t *dir; + char *netpath; + FILE *filep; + int len; - hash = 0; + if(filename == NULL) + Com_Error(ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed"); - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization" ); + // 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; } - if ( file == NULL ) { + 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] ) { + + // 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 { + + do + { // case and separator insensitive comparisons - if ( !FS_FilenameCompare( pakFile->name, filename ) ) { + if(!FS_FilenameCompare(pakFile->name, filename)) + { // found it! - return qtrue; + 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 ); - 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++; - } + else if(search->dir) + { + dir = search->dir; + + netpath = FS_BuildOSPath(dir->path, dir->gamedir, filename); + filep = fopen (netpath, "rb"); - // 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; + if(filep) + { + len = FS_fplength(filep); + fclose(filep); + + if(len) + return len; + else + return 1; + } + } + + return 0; } - // - // search through the path, one element at a time - // - *file = FS_HandleForFile(); fsh[*file].handleFiles.unique = uniqueFILE; + + // is the element a pak file? + if(search->pack) + { + hash = FS_HashFileName(filename, search->pack->hashSize); - 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] ) { + if(search->pack->hashTable[hash]) + { // disregard if it doesn't match one of the allowed pure pak files - if ( !FS_PakIsPure(search->pack) ) { - continue; + if(!FS_PakIsPure(search->pack)) + { + *file = 0; + return -1; } // look through all the pak file elements pak = search->pack; pakFile = pak->hashTable[hash]; - do { + + do + { // case and separator insensitive comparisons - if ( !FS_FilenameCompare( pakFile->name, filename ) ) { + 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); - + len = strlen(filename); + if (!(pak->referenced & FS_GENERAL_REF)) { - if(!FS_IsExt(filename, ".shader", l) && - !FS_IsExt(filename, ".txt", l) && - !FS_IsExt(filename, ".cfg", l) && - !FS_IsExt(filename, ".config", l) && - !FS_IsExt(filename, ".bot", l) && - !FS_IsExt(filename, ".arena", l) && - !FS_IsExt(filename, ".menu", l) && + 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 (!(pak->referenced & FS_QAGAME_REF) && strstr(filename, "game.qvm")) { + if(strstr(filename, "game.qvm")) pak->referenced |= FS_QAGAME_REF; - } - if (!(pak->referenced & FS_CGAME_REF) && strstr(filename, "cgame.qvm")) { + if(strstr(filename, "cgame.qvm")) pak->referenced |= FS_CGAME_REF; - } - if (!(pak->referenced & FS_UI_REF) && strstr(filename, "ui.qvm")) { + if(strstr(filename, "ui.qvm")) pak->referenced |= FS_UI_REF; - } - if ( uniqueFILE ) { + 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; + 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); } - Q_strncpyz( fsh[*file].name, filename, sizeof( fsh[*file].name ) ); + 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 ); + 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 ); + 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) + } + } + 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, ".dat", len) && // for journal files + !FS_IsDemoExt(filename, len)) // demos { - if(!FS_IsExt(filename, ".cfg", l) && // for config files - !FS_IsExt(filename, ".menu", l) && // menu files - !FS_IsExt(filename, ".game", l) && // menu files - !FS_IsExt(filename, ".dat", l) && // for journal files - !FS_IsDemoExt(filename, l)) // demos - { - continue; - } + *file = 0; + return -1; } + } - 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; - } + dir = search->dir; - 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 ); - } + netpath = FS_BuildOSPath(dir->path, dir->gamedir, filename); + filep = fopen(netpath, "rb"); - return FS_filelength (*file); - } + 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 + +Finds the file in the search path. +Returns filesize and an open FILE pointer. +Used for streaming data out of either a +separate file or a ZIP file. +=========== +*/ +long FS_FOpenFileRead(const char *filename, fileHandle_t *file, qboolean uniqueFILE) +{ + searchpath_t *search; + long len; + + if(!fs_searchpaths) + Com_Error(ERR_FATAL, "Filesystem call made without initialization"); + + for(search = fs_searchpaths; search; search = search->next) + { + len = FS_FOpenFileReadDir(filename, search, file, uniqueFILE); + + if(file == NULL) + { + if(len > 0) + return len; + } + else + { + if(len >= 0 && *file) + return len; + } + } #ifdef FS_MISSING - if (missingFiles) { + if(missingFiles) fprintf(missingFiles, "%s\n", filename); - } #endif - *file = 0; + + if(file) + *file = 0; + return -1; } +/* +================= +FS_FindVM -char *FS_FindDll( const char *filename ) { - searchpath_t *search; - directory_t *dir; +Find a suitable VM file in search path order. - if ( !fs_searchpaths ) { - Com_Error( ERR_FATAL, "Filesystem call made without initialization" ); - } +In each searchpath try: + - open DLL file if DLL loading enabled + - open QVM file - for ( search = fs_searchpaths ; search ; search = search->next ) { - if ( search->dir ) { - FILE *f; - char *netpath; +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; + pack_t *pack; + char dllName[MAX_OSPATH], qvmName[MAX_OSPATH]; + 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; - netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); - f = fopen( netpath, "rb" ); - if (f) { - fclose( f ); - return netpath; + + if(enableDll) + { + netpath = FS_BuildOSPath(dir->path, dir->gamedir, dllName); + + if(FS_FileInPathExists(netpath)) + { + Q_strncpyz(found, netpath, foundlen); + *startSearch = search; + + return VMI_NATIVE; + } + } + + 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 NULL; + return -1; } /* @@ -1543,17 +1707,20 @@ int FS_FileIsInPAK(const char *filename, int *pChecksum ) { /* ============ -FS_ReadFile +FS_ReadFileDir Filename are relative to the quake search path 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; + searchpath_t *search; byte* buf; qboolean isConfig; - int len; + long len; if ( !fs_searchpaths ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization" ); @@ -1610,8 +1777,19 @@ int FS_ReadFile( const char *qpath, void **buffer ) { isConfig = qfalse; } - // look for it in the filesystem or pack files - len = FS_FOpenFileRead( qpath, &h, qfalse ); + search = searchPath; + + if(search == NULL) + { + // look for it in the filesystem or pack files + 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 ( buffer ) { *buffer = NULL; @@ -1659,6 +1837,19 @@ int FS_ReadFile( const char *qpath, void **buffer ) { } /* +============ +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 ============= @@ -2514,21 +2705,40 @@ 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 ============ */ void FS_Which_f( void ) { searchpath_t *search; - char *netpath; - pack_t *pak; - fileInPack_t *pakFile; - directory_t *dir; - long hash; - FILE *temp; - char *filename; - char buf[ MAX_OSPATH ]; + char *filename; - hash = 0; filename = Cmd_Argv(1); if ( !filename[0] ) { @@ -2542,40 +2752,13 @@ void FS_Which_f( void ) { } // 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! - Com_Printf( "File \"%s\" found in \"%s\"\n", filename, pak->pakFilename ); - return; - } - 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); - Com_sprintf( buf, sizeof( buf ), "%s/%s", dir->path, dir->gamedir ); - FS_ReplaceSeparators( buf ); - Com_Printf( "File \"%s\" found at \"%s\"\n", filename, buf ); + for(search = fs_searchpaths; search; search = search->next) + { + if(FS_Which(filename, search)) return; - } } - Com_Printf( "File not found: \"%s\"\n", filename ); + + Com_Printf("File not found: \"%s\"\n", filename); return; } @@ -2605,7 +2788,7 @@ void FS_AddGameDirectory( const char *path, const char *dir ) { int i; searchpath_t *search; pack_t *pak; - char *pakfile; + char curpath[MAX_OSPATH + 1], *pakfile; int numfiles; char **pakfiles; @@ -2618,22 +2801,11 @@ void FS_AddGameDirectory( const char *path, const char *dir ) { 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 - pakfile = FS_BuildOSPath( path, dir, "" ); - pakfile[ strlen(pakfile) - 1 ] = 0; // strip the trailing slash + Q_strncpyz(curpath, FS_BuildOSPath(path, dir, ""), sizeof(curpath)); + 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 ); @@ -2641,8 +2813,10 @@ void FS_AddGameDirectory( const char *path, const char *dir ) { pakfile = FS_BuildOSPath( path, dir, pakfiles[i] ); if ( ( pak = FS_LoadZipFile( pakfile, pakfiles[i] ) ) == 0 ) continue; + + Q_strncpyz(pak->pakPathname, curpath, sizeof(pak->pakPathname)); // store the game name for downloading - strcpy(pak->pakGamename, dir); + Q_strncpyz(pak->pakGamename, dir, sizeof(pak->pakGamename)); fs_packFiles += pak->numfiles; @@ -2654,6 +2828,19 @@ void FS_AddGameDirectory( const char *path, const char *dir ) { // done 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; } /* diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index d09ee299..2cf2b27b 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -602,7 +602,7 @@ qboolean FS_FileExists( const char *file ); 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 ); qboolean FS_CompareZipChecksum(const char *zipfile); @@ -618,9 +618,9 @@ fileHandle_t FS_FCreateOpenPipeFile( const char *filename ); // will properly create any needed paths and deal with seperater character issues 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 ); -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 // 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 @@ -639,7 +639,8 @@ int FS_Read( void *buffer, int len, fileHandle_t f ); void FS_FCloseFile( fileHandle_t f ); // 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 // a null buffer will just return the file length without loading // as a quick check for existance. -1 length == not present @@ -656,7 +657,7 @@ void FS_FreeFile( void *buffer ); void FS_WriteFile( const char *qpath, const void *buffer, int size ); // 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 int FS_FTell( fileHandle_t f ); @@ -713,6 +714,7 @@ void FS_FilenameCompletion( const char *dir, const char *ext, qboolean stripExt, void(*callback)(const char *s), qboolean allowNonPureFilesOnDisk ); const char *FS_GetCurrentGameDir(void); +qboolean FS_Which(const char *filename, void *searchPath); /* ============================================================== diff --git a/src/qcommon/vm.c b/src/qcommon/vm.c index 5476a085..c53d8067 100644 --- a/src/qcommon/vm.c +++ b/src/qcommon/vm.c @@ -378,15 +378,20 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { // load the image Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name ); 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 ) { Com_Printf( "Failed.\n" ); VM_Free( vm ); + + Com_Printf(S_COLOR_YELLOW "Warning: Couldn't open VM file %s\n", filename); + return NULL; } // 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 ) { Com_Printf( "...which has vmMagic VM_MAGIC_VER2\n" ); @@ -401,9 +406,13 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { || header.h->bssLength < 0 || header.h->dataLength < 0 || header.h->litLength < 0 - || header.h->codeLength <= 0 ) { - VM_Free( vm ); - Com_Error( ERR_FATAL, "%s has bad header", filename ); + || header.h->codeLength <= 0 ) + { + 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 ) { // byte swap the header @@ -416,14 +425,21 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) { if ( header.h->bssLength < 0 || header.h->dataLength < 0 || header.h->litLength < 0 - || header.h->codeLength <= 0 ) { - VM_Free( vm ); - Com_Error( ERR_FATAL, "%s has bad header", filename ); + || header.h->codeLength <= 0 ) + { + VM_Free(vm); + FS_FreeFile(header.v); + + Com_Printf(S_COLOR_YELLOW "Warning: %s has bad header\n", filename); + return NULL; } } else { VM_Free( vm ); - Com_Error( ERR_FATAL, "%s does not have a recognisable " - "magic number in its header", filename ); + FS_FreeFile(header.v); + + 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 @@ -525,7 +541,9 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vmInterpret_t interpret ) { vm_t *vm; vmHeader_t *header; - int i, remaining; + int i, remaining, retval; + char filename[MAX_OSPATH]; + void *startSearch = NULL; if ( !module || !module[0] || !systemCalls ) { Com_Error( ERR_FATAL, "VM_Create: bad parms" ); @@ -554,25 +572,41 @@ vm_t *VM_Create( const char *module, intptr_t (*systemCalls)(intptr_t *), vm = &vmTable[i]; - Q_strncpyz( vm->name, module, sizeof( vm->name ) ); - vm->systemCall = systemCalls; + Q_strncpyz(vm->name, module, sizeof(vm->name)); - if ( interpret == VMI_NATIVE ) { - // try to load as a system dll - Com_Printf( "Loading dll file %s.\n", vm->name ); - vm->dllHandle = Sys_LoadDll( module, &vm->entryPoint, VM_DllSyscall ); - if ( vm->dllHandle ) { - return vm; + do + { + retval = FS_FindVM(&startSearch, filename, sizeof(filename), module, (interpret == VMI_NATIVE)); + + 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; + } + + Com_Printf("Failed loading dll, trying next\n"); } - - Com_Printf( "Failed to load dll, looking for qvm.\n" ); - interpret = VMI_COMPILED; - } - - // load the image - if( !( header = VM_LoadQVM( vm, qtrue ) ) ) { + else if(retval == VMI_COMPILED) + { + vm->searchPath = startSearch; + if((header = VM_LoadQVM(vm, qtrue))) + break; + + // VM_Free overwrites the name on failed load + Q_strncpyz(vm->name, module, sizeof(vm->name)); + } + } 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 vm->instructionCount = header->instructionCount; diff --git a/src/qcommon/vm_local.h b/src/qcommon/vm_local.h index c7270600..ee44133a 100644 --- a/src/qcommon/vm_local.h +++ b/src/qcommon/vm_local.h @@ -142,7 +142,8 @@ struct vm_s { //------------------------------------ - char name[MAX_QPATH]; + char name[MAX_QPATH]; + void *searchPath; // hint for FS_ReadFileDir() // for dynamic linked modules void *dllHandle; diff --git a/src/renderer/tr_public.h b/src/renderer/tr_public.h index 5126ab49..1a7d93c2 100644 --- a/src/renderer/tr_public.h +++ b/src/renderer/tr_public.h @@ -149,7 +149,7 @@ typedef struct { // a -1 return means the file does not exist // NULL can be passed for buf to just determine existance 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 ); char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound ); void (*FS_FreeFileList)( char **filelist ); diff --git a/src/sys/sys_main.c b/src/sys/sys_main.c index 07e8e395..5036009e 100644 --- a/src/sys/sys_main.c +++ b/src/sys/sys_main.c @@ -413,35 +413,23 @@ void Sys_UnloadDll( void *dllHandle ) Sys_LoadDll 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 (*systemcalls)(intptr_t, ...) ) + intptr_t (*systemcalls)(intptr_t, ...)) { - void *libHandle; - void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ); - char fname[MAX_OSPATH]; - char *netpath; + void *libHandle; + void (*dllEntry)(intptr_t (*syscallptr)(intptr_t, ...)); - 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(!netpath) { - 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() ); + if(!libHandle) + { + Com_Printf("Sys_LoadDll(%s) failed:\n\"%s\"\n", name, Sys_LibraryError()); return NULL; } |