From 3393e2d95c2bdd180bc47f2eb5631cecbe07d978 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Dec 2022 17:22:30 +0300 Subject: [PATCH] filesystem: implement directory entries caching, to avoid excessive directory listing syscalls to emulate case-insensitive filesystems * simplify game directory initialization code --- filesystem/dir.c | 331 ++++++++++++++++++++++++++++++- filesystem/filesystem.c | 261 ++++++++---------------- filesystem/filesystem_internal.h | 21 +- filesystem/pak.c | 19 +- filesystem/wad.c | 10 +- filesystem/zip.c | 17 +- 6 files changed, 439 insertions(+), 220 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index 9fa03be0..138dbdf0 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -1,5 +1,5 @@ /* -dir.c - directory operations +dir.c - caseinsensitive directory operations Copyright (C) 2022 Alibek Omarov, Velaron This program is free software: you can redistribute it and/or modify @@ -27,9 +27,309 @@ GNU General Public License for more details. #include "xash3d_mathlib.h" #include "common/com_strings.h" -static void FS_Close_DIR( searchpath_t *search ) +enum +{ + DIRENTRY_EMPTY_DIRECTORY = 0, // don't care if it's not directory or it's empty + DIRENTRY_NOT_SCANNED = -1, + DIRENTRY_CASEINSENSITIVE = -2, // directory is already caseinsensitive, just copy whatever is left +}; + +typedef struct dir_s +{ + string name; + int numentries; + struct dir_s *entries; // sorted +} dir_t; + +static int FS_SortDir( const void *_a, const void *_b ) +{ + const dir_t *a = _a; + const dir_t *b = _b; + return Q_stricmp( a->name, b->name ); +} + +static void FS_FreeDirEntries( dir_t *dir ) +{ + if( dir->entries ) + { + int i; + for( i = 0; i < dir->numentries; i++ ) + FS_FreeDirEntries( &dir->entries[i] ); + dir->entries = NULL; + } + + dir->numentries = DIRENTRY_NOT_SCANNED; +} + +static void FS_InitDirEntries( dir_t *dir, const stringlist_t *list ) +{ + int i; + + if( !list->numstrings ) + { + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + dir->entries = NULL; + return; + } + + dir->numentries = list->numstrings; + dir->entries = Mem_Malloc( fs_mempool, sizeof( dir_t ) * dir->numentries ); + + for( i = 0; i < list->numstrings; i++ ) + { + dir_t *entry = &dir->entries[i]; + Q_strncpy( entry->name, list->strings[i], sizeof( entry->name )); + entry->numentries = DIRENTRY_NOT_SCANNED; + entry->entries = NULL; + } + + qsort( dir->entries, dir->numentries, sizeof( dir->entries[0] ), FS_SortDir ); +} + +static void FS_PopulateDirEntries( dir_t *dir, const char *path ) { +#if XASH_WIN32 // Windows is always case insensitive + dir->numentries = DIRENTRY_CASEINSENSITIVE; + dir->entries = NULL; +#else + stringlist_t list; + if( !FS_SysFolderExists( path )) + { + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + dir->entries = NULL; + return; + } + + stringlistinit( &list ); + listdirectory( &list, path, false ); + FS_InitDirEntries( dir, &list ); + stringlistfreecontents( &list ); +#endif +} + +static int FS_FindDirEntry( dir_t *dir, const char *name ) +{ + int left, right; + + if( dir->numentries < 0 ) + return -1; + + // look for the file (binary search) + left = 0; + right = dir->numentries - 1; + while( left <= right ) + { + int middle = (left + right) / 2; + int diff; + + diff = Q_stricmp( dir->entries[middle].name, name ); + + // found it + if( !diff ) + return middle; + + // if we're too far in the list + if( diff > 0 ) + right = middle - 1; + else left = middle + 1; + } + return -1; +} + +static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) +{ + int i; + dir_t temp; + + FS_InitDirEntries( &temp, list ); + + // copy all entries that has the same name and has subentries + for( i = 0; i < dir->numentries; i++ ) + { + int j; + + // don't care about directories without subentries + if( dir->entries == NULL ) + continue; + + // try to find this directory in new tree + j = FS_FindDirEntry( &temp, dir->entries[i].name ); + + // not found, free memory + if( j < 0 ) + { + FS_FreeDirEntries( &dir->entries[i] ); + continue; + } + + // found directory, move all entries + temp.entries[j].numentries = dir->entries[j].numentries; + temp.entries[j].entries = dir->entries[j].entries; + } + + // now we can free old tree and replace it with temporary + Mem_Free( dir->entries ); + dir->numentries = temp.numentries; + dir->entries = temp.entries; +} + +static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *entryname ) +{ + stringlist_t list; + qboolean update = false; + int idx; + + stringlistinit( &list ); + listdirectory( &list, path, false ); + + // find the reason to update entries list + if( list.numstrings != dir->numentries ) + { + // small optimization to not search string in the list + // and directly go updating entries + update = true; + } + else + { + for( idx = 0; idx < list.numstrings; idx++ ) + { + if( !Q_stricmp( list.strings[idx], entryname )) + { + update = true; + break; + } + } + } + + if( !update ) + { + stringlistfreecontents( &list ); + return -1; + } + + FS_MergeDirEntries( dir, &list ); + stringlistfreecontents( &list ); + return FS_FindDirEntry( dir, entryname ); +} + +qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) +{ + const char *prev = path; + const char *next = Q_strchrnul( prev, PATH_SEPARATOR ); + size_t i = Q_strlen( dst ); // dst is expected to have searchpath filename + + while( true ) + { + char entryname[MAX_SYSPATH]; + int ret; + + // this subdirectory is case insensitive, just slam everything that's left + if( dir->numentries == DIRENTRY_CASEINSENSITIVE ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (caseinsensitive entry)\n", __FUNCTION__, path ); + return false; + } + } + + // populate cache if needed + if( dir->numentries == DIRENTRY_NOT_SCANNED ) + FS_PopulateDirEntries( dir, dst ); + + // get our entry name + Q_strncpy( entryname, prev, next - prev + 1 ); + ret = FS_FindDirEntry( dir, entryname ); + + // didn't found, but does it exists in FS? + if( ret < 0 ) + { + ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); + + if( ret < 0 ) + { + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if( createpath ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (create path)\n", __FUNCTION__, path ); + return false; + } + + return true; + } + return false; + } + } + + dir = &dir->entries[ret]; + ret = Q_strncpy( &dst[i], dir->name, len - i ); + + // file not found, rescan... + if( !FS_SysFileOrFolderExists( dst )) + { + // strip failed part + dst[i] = 0; + + ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); + + // file not found, exit... =/ + if( ret < 0 ) + { + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if( createpath ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (create path 2)\n", __FUNCTION__, path ); + return false; + } + + return true; + } + return false; + } + + dir = &dir->entries[ret]; + ret = Q_strncpy( &dst[i], dir->name, len - i ); + } + + i += ret; + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (appending fixed file name)\n", __FUNCTION__, path ); + return false; + } + + // end of string, found file, return + if( next[0] == '\0' ) + break; + + // move pointer one character forward, find next path split character + prev = next + 1; + next = Q_strchrnul( prev, PATH_SEPARATOR ); + i += Q_strncpy( &dst[i], PATH_SEPARATOR_STR, len - i ); + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (path separator)\n", __FUNCTION__, path ); + return false; + } + } + + return true; +} + +static void FS_Close_DIR( searchpath_t *search ) +{ + FS_FreeDirEntries( search->dir ); + Mem_Free( search->dir ); } static void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) @@ -37,14 +337,21 @@ static void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) Q_strncpy( dst, search->filename, size ); } -static int FS_FindFile_DIR( searchpath_t *search, const char *path ) +static int FS_FindFile_DIR( searchpath_t *search, const char *path, char *fixedname, size_t len ) { char netpath[MAX_SYSPATH]; - Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, path ); + Q_strncpy( netpath, search->filename, sizeof( netpath )); + if( !FS_FixFileCase( search->dir, path, netpath, sizeof( netpath ), false )) + return -1; - if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) ) ) + if( FS_SysFileExists( netpath, !FBitSet( search->flags, FS_CUSTOM_PATH ))) + { + // return fixed case file name only local for that searchpath + if( fixedname ) + Q_strncpy( fixedname, netpath + Q_strlen( search->filename ), len ); return 0; + } return -1; } @@ -63,7 +370,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char separator = Q_max( slash, backslash ); separator = Q_max( separator, colon ); - + basepathlength = separator ? (separator + 1 - pattern) : 0; basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); if( basepathlength ) memcpy( basepath, pattern, basepathlength ); @@ -100,6 +407,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char static int FS_FileTime_DIR( searchpath_t *search, const char *filename ) { + int time; char path[MAX_SYSPATH]; Q_snprintf( path, sizeof( path ), "%s%s", search->filename, filename ); @@ -127,9 +435,14 @@ void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int fla search->pfnFileTime = FS_FileTime_DIR; search->pfnFindFile = FS_FindFile_DIR; search->pfnSearch = FS_Search_DIR; + + // create cache root + search->dir = Mem_Malloc( fs_mempool, sizeof( dir_t )); + search->dir->name[0] = 0; // root has no filename, unused + FS_PopulateDirEntries( search->dir, path ); } -qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) +searchpath_t *FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) { searchpath_t *search; @@ -139,7 +452,7 @@ qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int fla { if( already_loaded ) *already_loaded = true; - return true; + return search; } } @@ -154,5 +467,5 @@ qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int fla Con_Printf( "Adding directory: %s\n", path ); - return true; + return search; } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index b6145267..c3795909 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -51,8 +51,8 @@ searchpath_t *fs_searchpaths = NULL; // chain char fs_rodir[MAX_SYSPATH]; char fs_rootdir[MAX_SYSPATH]; char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) +searchpath_t *fs_writepath; -static searchpath_t fs_directpath; // static direct path static char fs_basedir[MAX_SYSPATH]; // base game directory static char fs_gamedir[MAX_SYSPATH]; // game current directory #if !XASH_WIN32 @@ -160,7 +160,7 @@ void stringlistappend( stringlist_t *list, char *text ) list->numstrings++; } -static void stringlistsort( stringlist_t *list ) +void stringlistsort( stringlist_t *list ) { char *temp; int i, j; @@ -243,109 +243,6 @@ OTHER PRIVATE FUNCTIONS ============================================================================= */ -/* -================== -FS_FixFileCase - -emulate WIN32 FS behaviour when opening local file -================== -*/ -const char *FS_FixFileCase( const char *path ) -{ -#if defined __DOS__ & !defined __WATCOM_LFN__ - // not fix, but convert to 8.3 CAPS and rotate slashes - // it is still recommended to package game data - static char out[PATH_MAX]; - int i = 0; - int last = 0; - while(*path) - { - char c = *path++; - - if(c == '/') c = '\\'; - else c = toupper(c); - out[i++] = c; - if(c == '\\' || c == '.') - { - if( i - last > 8 ) - { - char *l = &out[last]; - l[6] = '~'; - l[7] = '1'; - l[8] = c; - i = last + 9; - } - last = i; - } - } - - out[i] = 0; - return out; -#elif !XASH_WIN32 && !XASH_IOS // assume case insensitive - DIR *dir; - struct dirent *entry; - char dirpath[PATH_MAX], *filename; - static char fixedpath[PATH_MAX]; - - if( !fs_caseinsensitive ) - return path; - - if( path[0] != '/' ) - Q_snprintf( dirpath, sizeof( dirpath ), "./%s", path ); - else Q_strncpy( dirpath, path, PATH_MAX ); - - filename = Q_strrchr( dirpath, '/' ); - - if( filename ) - *filename++ = '\0'; - else - { - filename = (char*)path; - Q_strcpy( dirpath, "."); - } - - /* android has too slow directory scanning, - so drop out some not useful cases */ - if( filename - dirpath > 4 ) - { - char *point; - // too many wad textures - if( !Q_stricmp( filename - 5, ".wad") ) - return path; - point = Q_strchr( filename, '.' ); - if( point ) - { - if( !Q_strcmp( point, ".mip") || !Q_strcmp( point, ".dds" ) || !Q_strcmp( point, ".ent" ) ) - return path; - if( filename[0] == '{' ) - return path; - } - } - - //Con_Reportf( "FS_FixFileCase: %s\n", path ); - - if( !( dir = opendir( dirpath ) ) ) - return path; - - while( ( entry = readdir( dir ) ) ) - { - if( Q_stricmp( entry->d_name, filename ) ) - continue; - - Q_snprintf( fixedpath, sizeof( fixedpath ), "%s/%s", dirpath, entry->d_name ); - - //Con_Reportf( "FS_FixFileCase: %s %s %s\n", dirpath, filename, entry->d_name ); - - path = fixedpath; - - break; - } - - closedir( dir ); -#endif - return path; -} - #if XASH_WIN32 /* ==================== @@ -397,7 +294,7 @@ static qboolean FS_AddArchive_Fullpath( const char *file, qboolean *already_load { const char *ext = COM_FileExtension( file ); - if( !Q_stricmp( ext, "pk3" ) ) + if( !Q_stricmp( ext, "pk3" )) return FS_AddZip_Fullpath( file, already_loaded, flags ); else if ( !Q_stricmp( ext, "pak" )) return FS_AddPak_Fullpath( file, already_loaded, flags ); @@ -416,13 +313,11 @@ then loads and adds pak1.pak pak2.pak ... */ void FS_AddGameDirectory( const char *dir, uint flags ) { - stringlist_t list; - searchpath_t *search; - string fullpath; - int i; + stringlist_t list; + searchpath_t *search; + char fullpath[MAX_SYSPATH]; + int i; - if( !FBitSet( flags, FS_NOWRITE_PATH )) - Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); stringlistinit( &list ); listdirectory( &list, dir, false ); stringlistsort( &list ); @@ -430,41 +325,36 @@ void FS_AddGameDirectory( const char *dir, uint flags ) // add any PAK package in the directory for( i = 0; i < list.numstrings; i++ ) { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "pak" )) + const char *ext = COM_FileExtension( list.strings[i] ); + + if( !Q_stricmp( ext, "pak" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddPak_Fullpath( fullpath, NULL, flags ); } - } - - // add any Zip package in the directory - for( i = 0; i < list.numstrings; i++ ) - { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" ) ) + else if( !Q_stricmp( ext, "pk3" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddZip_Fullpath( fullpath, NULL, flags ); } - } - - FS_AllowDirectPaths( true ); - - // add any WAD package in the directory - for( i = 0; i < list.numstrings; i++ ) - { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "wad" )) + else if( !Q_stricmp( ext, "wad" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + FS_AllowDirectPaths( true ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddWad_Fullpath( fullpath, NULL, flags ); + FS_AllowDirectPaths( false ); } } - stringlistfreecontents( &list ); - FS_AllowDirectPaths( false ); // add the directory to the search path // (unpacked files have the priority over packed files) - FS_AddDir_Fullpath( dir, NULL, flags ); + search = FS_AddDir_Fullpath( dir, NULL, flags ); + if( !FBitSet( flags, FS_NOWRITE_PATH )) + { + Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); + fs_writepath = search; + } } /* @@ -929,7 +819,7 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool } // make sure what gamedir is really exist - if( !FS_SysFolderExists( va( "%s"PATH_SPLITTER"%s", fs_rootdir, GameInfo->falldir ))) + if( !FS_SysFolderExists( va( "%s" PATH_SEPARATOR_STR "%s", fs_rootdir, GameInfo->falldir ))) GameInfo->falldir[0] = '\0'; } @@ -1278,7 +1168,7 @@ search for library, assume index is valid static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dllinfo_t *dllInfo ) { searchpath_t *search; - int index, start = 0, i, len; + int index, start = 0, i, len; fs_ext_path = directpath; @@ -1302,7 +1192,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll COM_DefaultExtension( dllInfo->shortPath, "."OS_LIB_EXT ); // apply ext if forget - search = FS_FindFile( dllInfo->shortPath, &index, false ); + search = FS_FindFile( dllInfo->shortPath, &index, NULL, 0, false ); if( !search && !directpath ) { @@ -1310,7 +1200,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll // trying check also 'bin' folder for indirect paths Q_strncpy( dllInfo->shortPath, dllname, sizeof( dllInfo->shortPath )); - search = FS_FindFile( dllInfo->shortPath, &index, false ); + search = FS_FindFile( dllInfo->shortPath, &index, NULL, 0, false ); if( !search ) return false; // unable to find } @@ -1433,8 +1323,11 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char for( i = 0; i < dirs.numstrings; i++ ) { - char *roPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, fs_rodir, dirs.strings[i] ); - char *rwPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, fs_rootdir, dirs.strings[i] ); + char roPath[MAX_SYSPATH]; + char rwPath[MAX_SYSPATH]; + + Q_snprintf( roPath, sizeof( roPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rodir, dirs.strings[i] ); + Q_snprintf( rwPath, sizeof( rwPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rootdir, dirs.strings[i] ); // check if it's a directory if( !FS_SysFolderExists( roPath )) @@ -1468,6 +1361,8 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char } // build list of game directories here + if( COM_CheckStringEmpty( fs_rodir )) + FS_AddGameDirectory( va( "%s/", fs_rodir ), FS_STATIC_PATH|FS_NOWRITE_PATH ); FS_AddGameDirectory( "./", FS_STATIC_PATH ); for( i = 0; i < dirs.numstrings; i++ ) @@ -1620,14 +1515,6 @@ file_t *FS_SysOpen( const char *filepath, const char *mode ) #if !XASH_WIN32 if( file->handle < 0 ) - { - const char *ffilepath = FS_FixFileCase( filepath ); - if( ffilepath != filepath ) - file->handle = open( ffilepath, mod|opt, 0666 ); - if( file->handle >= 0 ) - FS_BackupFileName( file, ffilepath, mod|opt ); - } - else FS_BackupFileName( file, filepath, mod|opt ); #endif @@ -1717,14 +1604,6 @@ qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ) 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; @@ -1777,6 +1656,26 @@ qboolean FS_SysFolderExists( const char *path ) #endif } +/* +============== +FS_SysFileOrFolderExists + +Check if filesystem entry exists at all, don't mind the type +============== +*/ +qboolean FS_SysFileOrFolderExists( const char *path ) +{ +#if XASH_WIN32 + return GetFileAttributes( path ) != -1; +#elif XASH_POSIX + struct stat buf; + return stat( path, &buf ) >= 0; +#else +#error +#endif + +} + /* ==================== FS_FindFile @@ -1787,7 +1686,7 @@ Return the searchpath where the file was found (or NULL) and the file index in the package if relevant ==================== */ -searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) +searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, qboolean gamedironly ) { searchpath_t *search; @@ -1799,7 +1698,7 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) if( gamedironly & !FBitSet( search->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - pack_ind = search->pfnFindFile( search, name ); + pack_ind = search->pfnFindFile( search, name, fixedname, len ); if( pack_ind >= 0 ) { if( index ) *index = pack_ind; @@ -1811,14 +1710,25 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) { char netpath[MAX_SYSPATH], dirpath[MAX_SYSPATH]; - Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SPLITTER, fs_rootdir ); + Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SEPARATOR_STR, fs_rootdir ); Q_snprintf( netpath, sizeof( netpath ), "%s%s", dirpath, name ); if( FS_SysFileExists( netpath, true )) { + static searchpath_t fs_directpath; + + // clear old dir cache + if( fs_directpath.pfnClose ) + fs_directpath.pfnClose( &fs_directpath ); + + // just copy the name, we don't do case sensitivity fix there + if( fixedname ) + { + Q_strncpy( fixedname, name, len ); + } FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); if( index != NULL ) - *index = -1; + *index = 0; return &fs_directpath; } } @@ -1839,16 +1749,17 @@ Look for a file in the search paths and open it in read-only mode */ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly ) { - searchpath_t *search; - int pack_ind; + searchpath_t *search; + char netpath[MAX_SYSPATH]; + int pack_ind; - search = FS_FindFile( filename, &pack_ind, gamedironly ); + search = FS_FindFile( filename, &pack_ind, netpath, sizeof( netpath ), gamedironly ); // not found? if( search == NULL ) return NULL; - return search->pfnOpenFile( search, filename, mode, pack_ind ); + return search->pfnOpenFile( search, netpath, mode, pack_ind ); } /* @@ -1884,7 +1795,7 @@ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) // open the file on disk directly Q_sprintf( real_path, "%s/%s", fs_writedir, filepath ); - FS_CreatePath( real_path );// Create directories up to the file + FS_CreatePath( real_path ); // Create directories up to the file return FS_SysOpen( real_path, mode ); } @@ -2450,9 +2361,7 @@ Look for a file in the packages and in the filesystem */ int GAME_EXPORT FS_FileExists( const char *filename, int gamedironly ) { - if( FS_FindFile( filename, NULL, gamedironly )) - return true; - return false; + return FS_FindFile( filename, NULL, NULL, 0, gamedironly ) != NULL; } /* @@ -2465,15 +2374,16 @@ return NULL for file in pack */ const char *FS_GetDiskPath( const char *name, qboolean gamedironly ) { + static char temp[MAX_SYSPATH]; searchpath_t *search; - search = FS_FindFile( name, NULL, gamedironly ); + search = FS_FindFile( name, NULL, temp, sizeof( temp ), gamedironly ); if( search ) { if( search->type != SEARCHPATH_PLAIN ) // file in pack or wad return NULL; - return va( "%s%s", search->filename, name ); + return temp; } return NULL; @@ -2526,13 +2436,14 @@ return time of creation file in seconds */ int FS_FileTime( const char *filename, qboolean gamedironly ) { - searchpath_t *search; - int pack_ind; + searchpath_t *search; + char netpath[MAX_SYSPATH]; + int pack_ind; - search = FS_FindFile( filename, &pack_ind, gamedironly ); + search = FS_FindFile( filename, &pack_ind, netpath, sizeof( netpath ), gamedironly ); if( !search ) return -1; // doesn't exist - return search->pfnFileTime( search, filename ); + return search->pfnFileTime( search, netpath ); } /* @@ -2550,6 +2461,10 @@ qboolean FS_Rename( const char *oldname, const char *newname ) if( !oldname || !newname || !*oldname || !*newname ) return false; + // no work done + if( !Q_stricmp( oldname, newname )) + return true; + Q_snprintf( oldpath, sizeof( oldpath ), "%s%s", fs_writedir, oldname ); Q_snprintf( newpath, sizeof( newpath ), "%s%s", fs_writedir, newname ); diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index b2473e4a..66e0886d 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -24,6 +24,7 @@ extern "C" { #endif +typedef struct dir_s dir_t; typedef struct zip_s zip_t; typedef struct pack_s pack_t; typedef struct wfile_s wfile_t; @@ -72,6 +73,7 @@ typedef struct searchpath_s union { + dir_t *dir; pack_t *pack; wfile_t *wad; zip_t *zip; @@ -83,7 +85,7 @@ typedef struct searchpath_s void (*pfnClose)( struct searchpath_s *search ); file_t *(*pfnOpenFile)( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ); int (*pfnFileTime)( struct searchpath_s *search, const char *filename ); - int (*pfnFindFile)( struct searchpath_s *search, const char *path ); + int (*pfnFindFile)( struct searchpath_s *search, const char *path, char *fixedname, size_t len ); void (*pfnSearch)( struct searchpath_s *search, stringlist_t *list, const char *pattern, int caseinsensitive ); } searchpath_t; @@ -157,6 +159,13 @@ qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ) qboolean CRC32_File( dword *crcvalue, const char *filename ); qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] ); +// stringlist ops +void stringlistinit( stringlist_t *list ); +void stringlistfreecontents( stringlist_t *list ); +void stringlistappend( stringlist_t *list, char *text ); +void stringlistsort( stringlist_t *list ); +void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); + // filesystem ops int FS_FileExists( const char *filename, int gamedironly ); int FS_FileTime( const char *filename, qboolean gamedironly ); @@ -165,19 +174,15 @@ qboolean FS_Rename( const char *oldname, const char *newname ); qboolean FS_Delete( const char *path ); qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); -void stringlistinit( stringlist_t *list ); -void stringlistfreecontents( stringlist_t *list ); -void stringlistappend( stringlist_t *list, char *text ); -void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); void FS_CreatePath( char *path ); qboolean FS_SysFolderExists( const char *path ); +qboolean FS_SysFileOrFolderExists( const char *path ); file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly ); int FS_SysFileTime( const char *filename ); file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len ); file_t *FS_SysOpen( const char *filepath, const char *mode ); -const char *FS_FixFileCase( const char *path ); -searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); +searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, qboolean gamedironly ); // // pak.c @@ -206,7 +211,7 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int // // dir.c // -qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); +searchpath_t *FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ); #ifdef __cplusplus diff --git a/filesystem/pak.c b/filesystem/pak.c index 8f3ee7d2..4efc3191 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -104,15 +104,6 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) packhandle = open( packfile, O_RDONLY|O_BINARY ); -#if !XASH_WIN32 - if( packhandle < 0 ) - { - const char *fpackfile = FS_FixFileCase( packfile ); - if( fpackfile != packfile ) - packhandle = open( fpackfile, O_RDONLY|O_BINARY ); - } -#endif - if( packhandle < 0 ) { Con_Reportf( "%s couldn't open: %s\n", packfile, strerror( errno )); @@ -210,7 +201,7 @@ FS_FindFile_PAK =========== */ -static int FS_FindFile_PAK( searchpath_t *search, const char *path ) +static int FS_FindFile_PAK( searchpath_t *search, const char *path, char *fixedname, size_t len ) { int left, right, middle; @@ -227,6 +218,8 @@ static int FS_FindFile_PAK( searchpath_t *search, const char *path ) // Found it if( !diff ) { + if( fixedname ) + Q_strncpy( fixedname, search->pack->files[middle].name, len ); return middle; } @@ -359,8 +352,6 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int if( pak ) { - string fullpath; - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); Q_strncpy( search->filename, pakfile, sizeof( search->filename )); search->pack = pak; @@ -384,7 +375,9 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int { if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" )) { - Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name ); + char fullpath[MAX_SYSPATH]; + + Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", pakfile, pak->files[i].name ); FS_AddWad_Fullpath( fullpath, NULL, flags ); } } diff --git a/filesystem/wad.c b/filesystem/wad.c index 6b94610d..adff3ad1 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -299,10 +299,6 @@ static wfile_t *W_Open( const char *filename, int *error ) wad->handle = FS_Open( basename, "rb", false ); - // HACKHACK: try to open WAD by full path for RoDir, when searchpaths are not ready - if( COM_CheckStringEmpty( fs_rodir ) && fs_ext_path && wad->handle == NULL ) - wad->handle = FS_SysOpen( filename, "rb" ); - if( wad->handle == NULL ) { Con_Reportf( S_ERROR "W_Open: couldn't open %s\n", filename ); @@ -430,7 +426,7 @@ FS_FindFile_WAD =========== */ -static int FS_FindFile_WAD( searchpath_t *search, const char *path ) +static int FS_FindFile_WAD( searchpath_t *search, const char *path, char *fixedname, size_t len ) { dlumpinfo_t *lump; signed char type = W_TypeFromExt( path ); @@ -469,6 +465,8 @@ static int FS_FindFile_WAD( searchpath_t *search, const char *path ) if( lump ) { + if( fixedname ) + Q_strncpy( fixedname, lump->name, len ); return lump - search->wad->lumps; } @@ -677,7 +675,7 @@ byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamed searchpath_t *search; int index; - search = FS_FindFile( path, &index, gamedironly ); + search = FS_FindFile( path, &index, NULL, 0, gamedironly ); if( search && search->type == SEARCHPATH_WAD ) return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); return NULL; diff --git a/filesystem/zip.c b/filesystem/zip.c index c1edd88c..e5171f12 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -202,15 +202,6 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) zip->handle = open( zipfile, O_RDONLY|O_BINARY ); -#if !XASH_WIN32 - if( zip->handle < 0 ) - { - const char *fzipfile = FS_FixFileCase( zipfile ); - if( fzipfile != zipfile ) - zip->handle = open( fzipfile, O_RDONLY|O_BINARY ); - } -#endif - if( zip->handle < 0 ) { Con_Reportf( S_ERROR "%s couldn't open\n", zipfile ); @@ -439,7 +430,7 @@ byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamediron if( sizeptr ) *sizeptr = 0; - search = FS_FindFile( path, &index, gamedironly ); + search = FS_FindFile( path, &index, NULL, 0, gamedironly ); if( !search || search->type != SEARCHPATH_ZIP ) return NULL; @@ -593,7 +584,7 @@ FS_FindFile_ZIP =========== */ -int FS_FindFile_ZIP( searchpath_t *search, const char *path ) +int FS_FindFile_ZIP( searchpath_t *search, const char *path, char *fixedname, size_t len ) { int left, right, middle; @@ -609,7 +600,11 @@ int FS_FindFile_ZIP( searchpath_t *search, const char *path ) // Found it if( !diff ) + { + if( fixedname ) + Q_strncpy( fixedname, search->zip->files[middle].name, len ); return middle; + } // if we're too far in the list if( diff > 0 )