diff --git a/engine/common/common.h b/engine/common/common.h index 26f2e84d..d554928a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -191,6 +191,7 @@ typedef enum #define FS_STATIC_PATH 1 // FS_ClearSearchPath will be ignore this path #define FS_NOWRITE_PATH 2 // default behavior - last added gamedir set as writedir. This flag disables it #define FS_GAMEDIR_PATH 4 // just a marker for gamedir path +#define FS_CUSTOM_PATH 8 // custom directory #define GI SI.GameInfo #define FS_Gamedir() SI.GameInfo->gamedir diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 2093b981..b0bc7153 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -14,10 +14,15 @@ GNU General Public License for more details. */ #include -#include #include -#include #include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif #include "common.h" #include "wadfile.h" #include "filesystem.h" @@ -108,7 +113,10 @@ searchpath_t fs_directpath; // static direct path char fs_basedir[MAX_SYSPATH]; // base game directory char fs_gamedir[MAX_SYSPATH]; // game current directory char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) -qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes +qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes +#ifndef _WIN32 +qboolean fs_caseinsensitive = true; // try to search missing files +#endif static const wadtype_t wad_hints[10]; static void FS_InitMemory( void ); @@ -117,7 +125,7 @@ static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char match static dpackfile_t *FS_AddFileToPack( const char* name, pack_t *pack, long offset, long size ); static byte *W_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); static wfile_t *W_Open( const char *filename, int *errorcode ); -static qboolean FS_SysFileExists( const char *path ); +static qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ); static qboolean FS_SysFolderExists( const char *path ); static long FS_SysFileTime( const char *filename ); static char W_TypeFromExt( const char *lumpname ); @@ -256,14 +264,21 @@ static void listlowercase( stringlist_t *list ) } } -static void listdirectory( stringlist_t *list, const char *path ) +static void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ) { - char pattern[4096]; + int i; + signed char *c; +#ifdef _WIN32 + char pattern[4096]; struct _finddata_t n_file; - long hFile; + int hFile; +#else + DIR *dir; + struct dirent *entry; +#endif - Q_strncpy( pattern, path, sizeof( pattern )); - Q_strncat( pattern, "*", sizeof( pattern )); +#ifdef _WIN32 + Q_snprintf( pattern, sizeof( pattern ), "%s*", path ); // ask for the directory listing handle hFile = _findfirst( pattern, &n_file ); @@ -271,14 +286,23 @@ static void listdirectory( stringlist_t *list, const char *path ) // start a new chain with the the first name stringlistappend( list, n_file.name ); - // iterate through the directory while( _findnext( hFile, &n_file ) == 0 ) stringlistappend( list, n_file.name ); _findclose( hFile ); +#else + if( !( dir = opendir( path ) ) ) + return; + + // iterate through the directory + while( ( entry = readdir( dir ) )) + stringlistappend( list, entry->d_name ); + closedir( dir ); +#endif - // g-cont. disabled for some reasons -// listlowercase( list ); + // convert names to lowercase because windows doesn't care, but pattern matching code often does + if( lowercase ) + listlowercase( list ); } /* @@ -288,6 +312,71 @@ OTHER PRIVATE FUNCTIONS ============================================================================= */ +/* +================== +FS_FixFileCase + +emulate WIN32 FS behaviour when opening local file +================== +*/ +static const char *FS_FixFileCase( const char *path ) +{ +#if !defined _WIN32 && !TARGET_OS_IPHONE // assume case insensitive + DIR *dir; struct dirent *entry; + char path2[PATH_MAX], *fname; + + if( !fs_caseinsensitive ) + return path; + + Q_snprintf( path2, sizeof( path2 ), "./%s", path ); + + fname = Q_strrchr( path2, '/' ); + + if( fname ) + *fname++ = 0; + else + { + fname = (char*)path; + Q_strcpy( path2, "."); + } + + /* android has too slow directory scanning, + so drop out some not useful cases */ + if( fname - path2 > 4 ) + { + char *point; + // too many wad textures + if( !Q_stricmp( fname - 5, ".wad") ) + return path; + point = Q_strchr( fname, '.' ); + if( point ) + { + if( !Q_strcmp( point, ".mip") || !Q_strcmp( point, ".dds" ) || !Q_strcmp( point, ".ent" ) ) + return path; + if( fname[0] == '{' ) + return path; + } + } + + //MsgDev( D_NOTE, "FS_FixFileCase: %s\n", path ); + + if( !( dir = opendir( path2 ) ) ) + return path; + + while( ( entry = readdir( dir ) ) ) + { + if( Q_stricmp( entry->d_name, fname ) ) + continue; + + path = va( "%s/%s", path2, entry->d_name ); + //MsgDev( D_NOTE, "FS_FixFileCase: %s %s %s\n", path2, fname, entry->d_name ); + break; + } + closedir( dir ); +#endif + return path; +} + /* ==================== FS_AddFileToPack @@ -412,6 +501,15 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error ) packhandle = open( packfile, O_RDONLY|O_BINARY ); +#ifndef _WIN32 + if( packhandle < 0 ) + { + const char *fpackfile = FS_FixFileCase( packfile ); + if( fpackfile!= packfile ) + packhandle = open( fpackfile, O_RDONLY|O_BINARY ); + } +#endif + if( packhandle < 0 ) { MsgDev( D_NOTE, "%s couldn't open\n", packfile ); @@ -614,7 +712,7 @@ void FS_AddGameDirectory( const char *dir, int flags ) Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); stringlistinit( &list ); - listdirectory( &list, dir ); + listdirectory( &list, dir, false ); stringlistsort( &list ); // add any PAK package in the directory @@ -1351,11 +1449,16 @@ void FS_Init( void ) Cmd_AddCommand( "fs_path", FS_Path_f, "show filesystem search pathes" ); Cmd_AddCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); +#ifndef _WIN32 + if( Sys_CheckParm( "-casesensitive" ) ) + fs_caseinsensitive = false; +#endif + // ignore commandlineoption "-game" for other stuff if( host.type == HOST_NORMAL || host.type == HOST_DEDICATED ) { stringlistinit( &dirs ); - listdirectory( &dirs, "./" ); + listdirectory( &dirs, "./", false ); stringlistsort( &dirs ); SI.numgames = 0; @@ -1513,6 +1616,15 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode ) file->handle = open( filepath, mod|opt, 0666 ); +#ifndef _WIN32 + if( file->handle < 0 ) + { + const char *ffilepath = FS_FixFileCase( filepath ); + if( ffilepath != filepath ) + file->handle = open( ffilepath, mod|opt, 0666 ); + } +#endif + if( file->handle < 0 ) { Mem_Free( file ); @@ -1568,8 +1680,9 @@ FS_SysFileExists Look for a file in the filesystem only ================== */ -qboolean FS_SysFileExists( const char *path ) +qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ) { +#ifdef _WIN32 int desc; if(( desc = open( path, O_RDONLY|O_BINARY )) < 0 ) @@ -1577,6 +1690,25 @@ qboolean FS_SysFileExists( const char *path ) close( desc ); return true; +#else + int ret; + struct stat buf; + + ret = stat( path, &buf ); + + // speedup custom path search + if( caseinsensitive && ( ret < 0 ) ) + { + const char *fpath = FS_FixFileCase( path ); + if( fpath != path ) + ret = stat( fpath, &buf ); + } + + if( ret < 0 ) + return false; + + return S_ISREG( buf.st_mode ); +#endif } /* @@ -1588,9 +1720,28 @@ Look for a existing folder */ qboolean FS_SysFolderExists( const char *path ) { +#ifdef _WIN32 DWORD dwFlags = GetFileAttributes( path ); - return ( dwFlags != -1 ) && FBitSet( dwFlags, FILE_ATTRIBUTE_DIRECTORY ); + return ( dwFlags != -1 ) && ( dwFlags & FILE_ATTRIBUTE_DIRECTORY ); +#else + DIR *dir = opendir( path ); + + if( dir ) + { + closedir( dir ); + return true; + } + else if( (errno == ENOENT) || (errno == ENOTDIR) ) + { + return false; + } + else + { + MsgDev( D_ERROR, "FS_SysFolderExists: problem while opening dir: %s\n", strerror( errno ) ); + return false; + } +#endif } /* @@ -1693,7 +1844,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir Q_sprintf( netpath, "%s%s", search->filename, name ); - if( FS_SysFileExists( netpath )) + if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) { if( index != NULL ) *index = -1; return search; @@ -1714,7 +1865,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir Q_strcat( search->filename, "\\" ); Q_snprintf( netpath, MAX_SYSPATH, "%s%s", search->filename, name ); - if( FS_SysFileExists( netpath )) + if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) { if( index != NULL ) *index = -1; @@ -1730,7 +1881,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir Q_strcat( search->filename, "\\" ); Q_snprintf( netpath, MAX_SYSPATH, "%s%s", search->filename, name ); - if( FS_SysFileExists( netpath )) + if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) { if( index != NULL ) *index = -1; @@ -2686,7 +2837,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) // get a directory listing and look at each name Q_sprintf( netpath, "%s%s", searchpath->filename, basepath ); stringlistinit( &dirlist ); - listdirectory( &dirlist, netpath ); + listdirectory( &dirlist, netpath, caseinsensitive ); for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) { @@ -3200,4 +3351,4 @@ static byte *W_LoadFile( const char *path, long *lumpsizeptr, qboolean gamediron if( search && search->wad ) return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); return NULL; -} \ No newline at end of file +}