mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-15 05:29:51 +01:00
filesystem: implement directory entries caching, to avoid excessive directory listing syscalls to emulate case-insensitive filesystems
* simplify game directory initialization code
This commit is contained in:
parent
e1ea3387ee
commit
3393e2d95c
331
filesystem/dir.c
331
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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 )
|
||||
|
Loading…
Reference in New Issue
Block a user