mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-06 04:05:11 +08:00
Compare commits
6 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c9b62cb03 | ||
|
|
9ff6eed0bf | ||
|
|
b6196c3c95 | ||
|
|
1ee2186f72 | ||
|
|
6099d5eadd | ||
|
|
3d68e13585 |
@@ -85,6 +85,9 @@ These strings are specific to Xash3D FWGS.
|
|||||||
As Xash3D accidentally supports GoldSrc games, it also supports parsing liblist.gam.\
|
As Xash3D accidentally supports GoldSrc games, it also supports parsing liblist.gam.\
|
||||||
Xash3D will use this file if gameinfo.txt is absent, or if its modification timestamp is older than liblist.gam.
|
Xash3D will use this file if gameinfo.txt is absent, or if its modification timestamp is older than liblist.gam.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Starting from January 2025, Xash3D FWGS doesn't automatically generate gameinfo.txt from liblist.gam. The key conversion table still remains but if you wish to use gameinfo.txt instead of liblist.gam, you can execute `fs_make_gameinfo` in console.
|
||||||
|
|
||||||
For game creators who plan supporting only Xash3D, using this file is not recommended.
|
For game creators who plan supporting only Xash3D, using this file is not recommended.
|
||||||
|
|
||||||
The table below defines conversion rules from liblist.gam to gameinfo.txt. Some keys' interpretation does differ from `gameinfo.txt`, in this case a note will be left. If `liblist.gam` key isn't present in this table, it's ignored.
|
The table below defines conversion rules from liblist.gam to gameinfo.txt. Some keys' interpretation does differ from `gameinfo.txt`, in this case a note will be left. If `liblist.gam` key isn't present in this table, it's ignored.
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ static void FS_Path_f_( void )
|
|||||||
FS_Path_f();
|
FS_Path_f();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FS_MakeGameInfo_f( void )
|
||||||
|
{
|
||||||
|
g_fsapi.MakeGameInfo();
|
||||||
|
}
|
||||||
|
|
||||||
static const fs_interface_t fs_memfuncs =
|
static const fs_interface_t fs_memfuncs =
|
||||||
{
|
{
|
||||||
Con_Printf,
|
Con_Printf,
|
||||||
@@ -272,6 +277,7 @@ void FS_Init( const char *basedir )
|
|||||||
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
|
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
|
||||||
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
|
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
|
||||||
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
||||||
|
Cmd_AddRestrictedCommand( "fs_make_gameinfo", FS_MakeGameInfo_f, "create gameinfo.txt for current running game" );
|
||||||
|
|
||||||
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
|
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
|
||||||
host.gamedll[0] = 0;
|
host.gamedll[0] = 0;
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path )
|
|||||||
}
|
}
|
||||||
|
|
||||||
stringlistinit( &list );
|
stringlistinit( &list );
|
||||||
listdirectory( &list, path );
|
listdirectory( &list, path, false );
|
||||||
if( !list.numstrings )
|
if( !list.numstrings )
|
||||||
{
|
{
|
||||||
dir->numentries = DIRENTRY_EMPTY_DIRECTORY;
|
dir->numentries = DIRENTRY_EMPTY_DIRECTORY;
|
||||||
@@ -225,7 +225,7 @@ static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *e
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
stringlistinit( &list );
|
stringlistinit( &list );
|
||||||
listdirectory( &list, path );
|
listdirectory( &list, path, false );
|
||||||
|
|
||||||
if( list.numstrings == 0 ) // empty directory
|
if( list.numstrings == 0 ) // empty directory
|
||||||
{
|
{
|
||||||
@@ -421,7 +421,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char
|
|||||||
}
|
}
|
||||||
|
|
||||||
stringlistinit( &dirlist );
|
stringlistinit( &dirlist );
|
||||||
listdirectory( &dirlist, netpath );
|
listdirectory( &dirlist, netpath, false );
|
||||||
|
|
||||||
Q_strncpy( temp, basepath, sizeof( temp ));
|
Q_strncpy( temp, basepath, sizeof( temp ));
|
||||||
|
|
||||||
|
|||||||
@@ -225,18 +225,13 @@ static void listlowercase( stringlist_t *list )
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void listdirectory( stringlist_t *list, const char *path )
|
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only )
|
||||||
{
|
{
|
||||||
#if XASH_WIN32
|
#if XASH_WIN32
|
||||||
char pattern[4096];
|
char pattern[4096];
|
||||||
struct _finddata_t n_file;
|
struct _finddata_t n_file;
|
||||||
intptr_t hFile;
|
intptr_t hFile;
|
||||||
#else
|
|
||||||
DIR *dir;
|
|
||||||
struct dirent *entry;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if XASH_WIN32
|
|
||||||
Q_snprintf( pattern, sizeof( pattern ), "%s/*", path );
|
Q_snprintf( pattern, sizeof( pattern ), "%s/*", path );
|
||||||
|
|
||||||
// ask for the directory listing handle
|
// ask for the directory listing handle
|
||||||
@@ -247,15 +242,33 @@ void listdirectory( stringlist_t *list, const char *path )
|
|||||||
stringlistappend( list, n_file.name );
|
stringlistappend( list, n_file.name );
|
||||||
// iterate through the directory
|
// iterate through the directory
|
||||||
while( _findnext( hFile, &n_file ) == 0 )
|
while( _findnext( hFile, &n_file ) == 0 )
|
||||||
|
{
|
||||||
|
if( dirs_only && !FBitSet( n_file.attrib, _A_SUBDIR ))
|
||||||
|
continue;
|
||||||
|
|
||||||
stringlistappend( list, n_file.name );
|
stringlistappend( list, n_file.name );
|
||||||
|
}
|
||||||
_findclose( hFile );
|
_findclose( hFile );
|
||||||
#else
|
#else
|
||||||
if( !( dir = opendir( path ) ) )
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
dir = opendir( path );
|
||||||
|
|
||||||
|
if( !dir )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// iterate through the directory
|
// iterate through the directory
|
||||||
while( ( entry = readdir( dir ) ))
|
while(( entry = readdir( dir )))
|
||||||
|
{
|
||||||
|
#if HAVE_DIRENT_D_TYPE
|
||||||
|
if( dirs_only && entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN )
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
stringlistappend( list, entry->d_name );
|
stringlistappend( list, entry->d_name );
|
||||||
|
}
|
||||||
|
|
||||||
closedir( dir );
|
closedir( dir );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -396,7 +409,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
stringlistinit( &list );
|
stringlistinit( &list );
|
||||||
listdirectory( &list, dir );
|
listdirectory( &list, dir, false );
|
||||||
stringlistsort( &list );
|
stringlistsort( &list );
|
||||||
|
|
||||||
for( archive = g_archives; archive->ext; archive++ )
|
for( archive = g_archives; archive->ext; archive++ )
|
||||||
@@ -508,12 +521,13 @@ FS_WriteGameInfo
|
|||||||
assume GameInfo is valid
|
assume GameInfo is valid
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
static qboolean FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
||||||
{
|
{
|
||||||
file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode
|
file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode
|
||||||
int i, write_ambients = false;
|
int i, write_ambients = false;
|
||||||
|
|
||||||
if( !f ) Sys_Error( "%s: can't write %s\n", __func__, filepath ); // may be disk-space is out?
|
if( !f )
|
||||||
|
return false;
|
||||||
|
|
||||||
FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||||
|
|
||||||
@@ -637,25 +651,50 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
|||||||
FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap );
|
FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap );
|
||||||
|
|
||||||
FS_Close( f ); // all done
|
FS_Close( f ); // all done
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir )
|
static void FS_MakeGameInfo( void )
|
||||||
|
{
|
||||||
|
if( FS_WriteGameInfo( "gameinfo.txt", FI.GameInfo ))
|
||||||
|
Con_Printf( "Successfully generated %s/gameinfo.txt\n", FI.GameInfo->gamefolder );
|
||||||
|
else
|
||||||
|
Con_Printf( S_ERROR "Can't open %s/gameinfo.txt for write\n", FI.GameInfo->gamefolder );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir, qboolean quake, time_t mtime )
|
||||||
{
|
{
|
||||||
memset( GameInfo, 0, sizeof( *GameInfo ));
|
memset( GameInfo, 0, sizeof( *GameInfo ));
|
||||||
|
|
||||||
// filesystem info
|
// filesystem info
|
||||||
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
|
GameInfo->mtime = mtime;
|
||||||
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
|
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
|
||||||
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
|
|
||||||
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
|
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
|
||||||
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
|
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
|
||||||
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
|
|
||||||
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
|
|
||||||
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
|
|
||||||
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
|
|
||||||
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
|
|
||||||
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
|
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
|
||||||
|
|
||||||
|
if( quake )
|
||||||
|
{
|
||||||
|
Q_strncpy( GameInfo->basedir, "id1", sizeof( GameInfo->basedir ));
|
||||||
|
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
|
||||||
|
Q_strncpy( GameInfo->startmap, "start", sizeof( GameInfo->startmap ));
|
||||||
|
Q_strncpy( GameInfo->dll_path, "bin", sizeof( GameInfo->dll_path ));
|
||||||
|
Q_strncpy( GameInfo->game_dll, "bin/progs.dll", sizeof( GameInfo->game_dll ));
|
||||||
|
Q_strncpy( GameInfo->game_dll_linux, "bin/progs.so", sizeof( GameInfo->game_dll_linux ));
|
||||||
|
Q_strncpy( GameInfo->game_dll_osx, "bin/progs.dylib", sizeof( GameInfo->game_dll_osx ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
|
||||||
|
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
|
||||||
|
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
|
||||||
|
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
|
||||||
|
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
|
||||||
|
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
|
||||||
|
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
|
||||||
|
}
|
||||||
|
|
||||||
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
|
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
|
||||||
GameInfo->max_tents = 500;
|
GameInfo->max_tents = 500;
|
||||||
GameInfo->max_beams = 128;
|
GameInfo->max_beams = 128;
|
||||||
@@ -940,36 +979,19 @@ static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
================
|
|
||||||
FS_CreateDefaultGameInfo
|
|
||||||
================
|
|
||||||
*/
|
|
||||||
static void FS_CreateDefaultGameInfo( const char *filename )
|
|
||||||
{
|
|
||||||
gameinfo_t defGI;
|
|
||||||
|
|
||||||
FS_InitGameInfo( &defGI, fs_basedir );
|
|
||||||
|
|
||||||
// make simple gameinfo.txt
|
|
||||||
FS_WriteGameInfo( filename, &defGI );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
================
|
================
|
||||||
FS_ParseLiblistGam
|
FS_ParseLiblistGam
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo )
|
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
||||||
{
|
{
|
||||||
char *afile;
|
char *afile = FS_LoadDirectFile( filename, NULL );
|
||||||
|
|
||||||
if( !GameInfo ) return false;
|
if( !afile )
|
||||||
afile = (char *)FS_LoadDirectFile( filename, NULL );
|
return false;
|
||||||
if( !afile ) return false;
|
|
||||||
|
|
||||||
FS_InitGameInfo( GameInfo, gamedir );
|
|
||||||
|
|
||||||
|
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
||||||
FS_ParseGenericGameInfo( GameInfo, afile, false );
|
FS_ParseGenericGameInfo( GameInfo, afile, false );
|
||||||
|
|
||||||
Mem_Free( afile );
|
Mem_Free( afile );
|
||||||
@@ -982,18 +1004,15 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g
|
|||||||
FS_ConvertGameInfo
|
FS_ConvertGameInfo
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path )
|
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path, gameinfo_t *gi, time_t liblist_mtime )
|
||||||
{
|
{
|
||||||
gameinfo_t GameInfo;
|
memset( gi, 0, sizeof( *gi ));
|
||||||
|
|
||||||
memset( &GameInfo, 0, sizeof( GameInfo ));
|
// liblist.gam to gameinfo.txt conversion is deprecated, only support for RwDir!
|
||||||
|
if( FS_ParseLiblistGam( liblist_path, gamedir, gi, liblist_mtime ))
|
||||||
if( FS_ParseLiblistGam( liblist_path, gamedir, &GameInfo ))
|
|
||||||
{
|
{
|
||||||
Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path );
|
Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path );
|
||||||
FS_WriteGameInfo( gameinfo_path, &GameInfo );
|
return FS_WriteGameInfo( gameinfo_path, gi );
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1004,16 +1023,14 @@ static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_pa
|
|||||||
FS_ReadGameInfo
|
FS_ReadGameInfo
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, gameinfo_t *GameInfo )
|
static qboolean FS_ReadGameInfo( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
||||||
{
|
{
|
||||||
char *afile;
|
char *afile = FS_LoadDirectFile( filename, NULL );
|
||||||
|
|
||||||
afile = (char *)FS_LoadFile( filepath, NULL, false );
|
|
||||||
if( !afile )
|
if( !afile )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FS_InitGameInfo( GameInfo, gamedir );
|
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
||||||
|
|
||||||
FS_ParseGenericGameInfo( GameInfo, afile, true );
|
FS_ParseGenericGameInfo( GameInfo, afile, true );
|
||||||
|
|
||||||
Mem_Free( afile );
|
Mem_Free( afile );
|
||||||
@@ -1029,20 +1046,32 @@ Checks if game directory resembles Quake Engine game directory
|
|||||||
(some of checks may as well work with Xash gamedirs, it's not a bug)
|
(some of checks may as well work with Xash gamedirs, it's not a bug)
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static qboolean FS_CheckForQuakeGameDir( const char *gamedir, qboolean direct )
|
static qboolean FS_CheckForQuakeGameDir( const char *gamedir )
|
||||||
{
|
{
|
||||||
// if directory contain config.cfg or progs.dat it's 100% gamedir
|
// if directory contain quake.rc or progs.dat it's 100% quake gamedir
|
||||||
// quake mods probably always archived but can missed config.cfg before first running
|
// quake mods probably always archived, so check pak0.pak too
|
||||||
const char *files[] = { "config.cfg", "progs.dat", "pak0.pak" };
|
const char *files[] = { "progs.dat", "quake.rc" };
|
||||||
|
char buf[MAX_SYSPATH];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
// try to read pak0.pak first, most quake mods are archived
|
||||||
|
if( Q_snprintf( buf, sizeof( buf ), "%s/pak0.pak", gamedir ) > 0 )
|
||||||
|
{
|
||||||
|
if( FS_SysFileExists( buf ))
|
||||||
|
{
|
||||||
|
if( FS_CheckForQuakePak( buf, files, sizeof( files ) / sizeof( files[0] )))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search it in the filesystem
|
||||||
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
||||||
{
|
{
|
||||||
char buf[MAX_VA_STRING];
|
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
||||||
|
{
|
||||||
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] );
|
if( FS_SysFileExists( buf ))
|
||||||
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false ))
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1055,7 +1084,7 @@ FS_CheckForXashGameDir
|
|||||||
Checks if game directory resembles Xash3D game directory
|
Checks if game directory resembles Xash3D game directory
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
static qboolean FS_CheckForXashGameDir( const char *gamedir )
|
||||||
{
|
{
|
||||||
// if directory contain gameinfo.txt or liblist.gam it's 100% gamedir
|
// if directory contain gameinfo.txt or liblist.gam it's 100% gamedir
|
||||||
const char *files[] = { "gameinfo.txt", "liblist.gam" };
|
const char *files[] = { "gameinfo.txt", "liblist.gam" };
|
||||||
@@ -1063,15 +1092,16 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
|||||||
|
|
||||||
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
||||||
{
|
{
|
||||||
char buf[MAX_SYSPATH];
|
char buf[MAX_SYSPATH];
|
||||||
|
|
||||||
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] );
|
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
||||||
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false ))
|
{
|
||||||
return true;
|
if( FS_SysFileExists( buf ))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1079,86 +1109,74 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
|||||||
FS_ParseGameInfo
|
FS_ParseGameInfo
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo )
|
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo, qboolean rodir )
|
||||||
{
|
{
|
||||||
string liblist_path, gameinfo_path;
|
char liblist_path[MAX_SYSPATH];
|
||||||
string default_gameinfo_path;
|
char gameinfo_path[MAX_SYSPATH];
|
||||||
qboolean haveUpdate = false;
|
char gamedir_path[MAX_SYSPATH];
|
||||||
|
time_t liblist_mtime = -1;
|
||||||
|
time_t gameinfo_mtime = -1;
|
||||||
|
|
||||||
Q_snprintf( default_gameinfo_path, sizeof( default_gameinfo_path ), "%s/gameinfo.txt", fs_basedir );
|
if( rodir )
|
||||||
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir );
|
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rodir, gamedir );
|
||||||
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir );
|
else
|
||||||
|
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rootdir, gamedir );
|
||||||
|
|
||||||
// here goes some RoDir magic...
|
if( !FS_CheckForXashGameDir( gamedir_path ))
|
||||||
if( COM_CheckStringEmpty( fs_rodir ))
|
|
||||||
{
|
{
|
||||||
string gameinfo_ro, liblist_ro;
|
// check if we need to generate gameinfo for Quake
|
||||||
fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime;
|
if( FS_CheckForQuakeGameDir( gamedir_path ))
|
||||||
|
|
||||||
FS_AllowDirectPaths( true );
|
|
||||||
|
|
||||||
Q_snprintf( gameinfo_ro, sizeof( gameinfo_ro ), "%s/%s/gameinfo.txt", fs_rodir, gamedir );
|
|
||||||
Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", fs_rodir, gamedir );
|
|
||||||
|
|
||||||
roLibListTime = FS_SysFileTime( liblist_ro );
|
|
||||||
roGameInfoTime = FS_SysFileTime( gameinfo_ro );
|
|
||||||
rwGameInfoTime = FS_SysFileTime( gameinfo_path );
|
|
||||||
|
|
||||||
// if rodir's liblist.gam newer than rwdir's gameinfo.txt, then convert it
|
|
||||||
if( roLibListTime > rwGameInfoTime )
|
|
||||||
{
|
{
|
||||||
haveUpdate = FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_ro );
|
// just generate stub gameinfo in memory
|
||||||
}
|
FS_InitGameInfo( GameInfo, gamedir, true, -1 );
|
||||||
// if rodir's gameinfo.txt newer than rwdir's gameinfo.txt, just copy the file
|
GameInfo->rodir = rodir;
|
||||||
else if( roGameInfoTime > rwGameInfoTime )
|
return true;
|
||||||
{
|
|
||||||
file_t *ro, *rw;
|
|
||||||
fs_offset_t ro_size;
|
|
||||||
|
|
||||||
// read & write as binary to copy the exact file
|
|
||||||
ro = FS_SysOpen( gameinfo_ro, "rb" );
|
|
||||||
rw = FS_SysOpen( gameinfo_path, "wb" );
|
|
||||||
|
|
||||||
FS_Seek( ro, 0, SEEK_END );
|
|
||||||
ro_size = FS_Tell( ro );
|
|
||||||
FS_Seek( ro, 0, SEEK_SET );
|
|
||||||
|
|
||||||
FS_FileCopy( rw, ro, ro_size );
|
|
||||||
|
|
||||||
FS_Close( rw );
|
|
||||||
FS_Close( ro );
|
|
||||||
|
|
||||||
haveUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FS_AllowDirectPaths( false );
|
// don't add empty or addon directories
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not update gameinfo.txt, if it was just copied from rodir's
|
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir_path );
|
||||||
// if user change liblist.gam update the gameinfo.txt
|
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir_path );
|
||||||
if( !haveUpdate && FS_FileTime( liblist_path, false ) > FS_FileTime( gameinfo_path, false ))
|
|
||||||
FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path );
|
|
||||||
|
|
||||||
// force to create gameinfo for specified game if missing
|
liblist_mtime = FS_SysFileTime( liblist_path );
|
||||||
if(( FS_CheckForQuakeGameDir( gamedir, false ) || !Q_stricmp( fs_gamedir, gamedir )) && !FS_FileExists( gameinfo_path, false ))
|
gameinfo_mtime = FS_SysFileTime( gameinfo_path );
|
||||||
|
|
||||||
|
// in this function we never write new files for RoDir compatibility
|
||||||
|
// since RoDir is only FWGS feature, do gameinfo.txt conversion only
|
||||||
|
// for RwDir for those who worked with original Xash3D
|
||||||
|
if( !rodir )
|
||||||
{
|
{
|
||||||
gameinfo_t tmpGameInfo;
|
// !!!only if we have both liblist.gam and gameinfo.txt try to convert liblist.gam to gameinfo.txt if it's newer!!!
|
||||||
memset( &tmpGameInfo, 0, sizeof( tmpGameInfo ));
|
if( liblist_mtime >= 0 && gameinfo_mtime >= 0 && liblist_mtime > gameinfo_mtime )
|
||||||
|
|
||||||
if( FS_ReadGameInfo( default_gameinfo_path, gamedir, &tmpGameInfo ))
|
|
||||||
{
|
{
|
||||||
// now we have copy of game info from basedir but needs to change gamedir
|
if( FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path, GameInfo, liblist_mtime ))
|
||||||
Con_DPrintf( "Convert %s to %s\n", default_gameinfo_path, gameinfo_path );
|
return true;
|
||||||
Q_strncpy( tmpGameInfo.gamefolder, gamedir, sizeof( tmpGameInfo.gamefolder ));
|
|
||||||
FS_WriteGameInfo( gameinfo_path, &tmpGameInfo );
|
|
||||||
}
|
}
|
||||||
else FS_CreateDefaultGameInfo( gameinfo_path );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !GameInfo || !FS_FileExists( gameinfo_path, false ))
|
// can we parse gameinfo.txt?
|
||||||
return false; // no dest
|
if( gameinfo_mtime >= 0 )
|
||||||
|
{
|
||||||
|
if( FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo, gameinfo_mtime ))
|
||||||
|
{
|
||||||
|
GameInfo->rodir = rodir;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo );
|
// can we parse liblist.gam?
|
||||||
|
if( liblist_mtime >= 0 )
|
||||||
|
{
|
||||||
|
if( FS_ParseLiblistGam( liblist_path, gamedir, GameInfo, liblist_mtime ))
|
||||||
|
{
|
||||||
|
GameInfo->rodir = rodir;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1197,7 +1215,7 @@ void FS_AddGameHierarchy( const char *dir, uint flags )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( COM_CheckStringEmpty( fs_rodir ) )
|
if( COM_CheckStringEmpty( fs_rodir ))
|
||||||
{
|
{
|
||||||
// append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH
|
// append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH
|
||||||
uint new_flags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH));
|
uint new_flags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH));
|
||||||
@@ -1266,10 +1284,12 @@ void FS_LoadGameInfo( const char *rootfolder )
|
|||||||
// lock uplevel of gamedir for read\write
|
// lock uplevel of gamedir for read\write
|
||||||
FS_AllowDirectPaths( false );
|
FS_AllowDirectPaths( false );
|
||||||
|
|
||||||
if( rootfolder ) Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir ));
|
if( rootfolder )
|
||||||
|
Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir ));
|
||||||
|
|
||||||
Con_Reportf( "%s( %s )\n", __func__, fs_gamedir );
|
Con_Reportf( "%s( %s )\n", __func__, fs_gamedir );
|
||||||
|
|
||||||
// clear any old pathes
|
// clear any old paths
|
||||||
FS_ClearSearchPath();
|
FS_ClearSearchPath();
|
||||||
|
|
||||||
// validate gamedir
|
// validate gamedir
|
||||||
@@ -1284,6 +1304,14 @@ void FS_LoadGameInfo( const char *rootfolder )
|
|||||||
|
|
||||||
FI.GameInfo = FI.games[i];
|
FI.GameInfo = FI.games[i];
|
||||||
|
|
||||||
|
if( FI.GameInfo->rodir )
|
||||||
|
{
|
||||||
|
// ensure we have directory in rwdir
|
||||||
|
char buf[MAX_SYSPATH + MAX_QPATH + 2]; // and have plenty of space
|
||||||
|
Q_snprintf( buf, sizeof( buf ), "%s/%s/", fs_rootdir, FI.GameInfo->gamefolder );
|
||||||
|
FS_CreatePath( buf );
|
||||||
|
}
|
||||||
|
|
||||||
FS_Rescan(); // create new filesystem
|
FS_Rescan(); // create new filesystem
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1344,7 +1372,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll
|
|||||||
{
|
{
|
||||||
string fixedname;
|
string fixedname;
|
||||||
searchpath_t *search;
|
searchpath_t *search;
|
||||||
int index, start = 0, i, len;
|
int index, start = 0, len;
|
||||||
|
|
||||||
// check for bad exports
|
// check for bad exports
|
||||||
if( !COM_CheckString( dllname ))
|
if( !COM_CheckString( dllname ))
|
||||||
@@ -1479,6 +1507,33 @@ static void *Sys_GetNativeObjectStub( const char *object )
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FS_ValidateDirectories( const char *path, qboolean *has_base_dir, qboolean *has_game_dir )
|
||||||
|
{
|
||||||
|
stringlist_t dirs;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
stringlistinit( &dirs );
|
||||||
|
listdirectory( &dirs, path, true );
|
||||||
|
stringlistsort( &dirs );
|
||||||
|
|
||||||
|
for( i = 0; i < dirs.numstrings; i++ )
|
||||||
|
{
|
||||||
|
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
|
||||||
|
*has_base_dir = true;
|
||||||
|
|
||||||
|
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
|
||||||
|
*has_game_dir = true;
|
||||||
|
|
||||||
|
if( *has_base_dir && *has_game_dir )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringlistfreecontents( &dirs );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
================
|
================
|
||||||
FS_Init
|
FS_Init
|
||||||
@@ -1486,11 +1541,9 @@ FS_Init
|
|||||||
*/
|
*/
|
||||||
qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir )
|
qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir )
|
||||||
{
|
{
|
||||||
stringlist_t dirs;
|
stringlist_t dirs;
|
||||||
qboolean hasBaseDir = false;
|
int i, rodir_num_games;
|
||||||
qboolean hasGameDir = false;
|
char buf[MAX_SYSPATH];
|
||||||
int i;
|
|
||||||
char buf[MAX_VA_STRING];
|
|
||||||
|
|
||||||
FS_InitMemory();
|
FS_InitMemory();
|
||||||
|
|
||||||
@@ -1503,63 +1556,40 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
|||||||
Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir ));
|
Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir ));
|
||||||
Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir ));
|
Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir ));
|
||||||
|
|
||||||
// add readonly directories first
|
// validate user input
|
||||||
if( COM_CheckStringEmpty( fs_rodir ))
|
if( COM_CheckStringEmpty( fs_rodir ) && !Q_stricmp( fs_rodir, fs_rootdir ))
|
||||||
{
|
{
|
||||||
if( !Q_stricmp( fs_rodir, fs_rootdir ))
|
Sys_Error( "RoDir and default rootdir can't point to same directory!" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for game directories in RwDir first
|
||||||
|
// this whole check only required to fallback to base directory
|
||||||
|
// (i.e. default game directory hardcoded in the executable file)
|
||||||
|
{
|
||||||
|
qboolean has_base_dir = false;
|
||||||
|
qboolean has_game_dir = false;
|
||||||
|
|
||||||
|
FS_ValidateDirectories( "./", &has_base_dir, &has_game_dir );
|
||||||
|
|
||||||
|
if( !has_game_dir )
|
||||||
{
|
{
|
||||||
Sys_Error( "RoDir and default rootdir can't point to same directory!" );
|
// look for game directories in RoDir now
|
||||||
return false;
|
if( COM_CheckStringEmpty( fs_rodir ))
|
||||||
}
|
FS_ValidateDirectories( fs_rodir, &has_base_dir, &has_game_dir );
|
||||||
|
|
||||||
stringlistinit( &dirs );
|
if( !has_game_dir )
|
||||||
listdirectory( &dirs, fs_rodir );
|
|
||||||
stringlistsort( &dirs );
|
|
||||||
|
|
||||||
for( i = 0; i < dirs.numstrings; i++ )
|
|
||||||
{
|
|
||||||
char roPath[MAX_SYSPATH];
|
|
||||||
|
|
||||||
Q_snprintf( roPath, sizeof( roPath ), "%s/%s/", fs_rodir, dirs.strings[i] );
|
|
||||||
|
|
||||||
// check if it's a directory
|
|
||||||
if( !FS_SysFolderExists( roPath ))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// check if it's gamedir
|
|
||||||
if( FS_CheckForXashGameDir( roPath, true ) || FS_CheckForQuakeGameDir( roPath, true ))
|
|
||||||
{
|
{
|
||||||
char rwPath[MAX_SYSPATH];
|
Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
|
||||||
|
|
||||||
Q_snprintf( rwPath, sizeof( rwPath ), "%s/%s/", fs_rootdir, dirs.strings[i] );
|
// revert to base game directory
|
||||||
FS_CreatePath( rwPath );
|
if( has_base_dir )
|
||||||
|
Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stringlistfreecontents( &dirs );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate directories
|
// now start building first level of directory hierarchy
|
||||||
stringlistinit( &dirs );
|
|
||||||
listdirectory( &dirs, "./" );
|
|
||||||
stringlistsort( &dirs );
|
|
||||||
|
|
||||||
for( i = 0; i < dirs.numstrings; i++ )
|
|
||||||
{
|
|
||||||
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
|
|
||||||
hasBaseDir = true;
|
|
||||||
|
|
||||||
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
|
|
||||||
hasGameDir = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !hasGameDir )
|
|
||||||
{
|
|
||||||
Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
|
|
||||||
if( hasBaseDir ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
|
|
||||||
}
|
|
||||||
|
|
||||||
// build list of game directories here
|
|
||||||
if( COM_CheckStringEmpty( fs_rodir ))
|
if( COM_CheckStringEmpty( fs_rodir ))
|
||||||
{
|
{
|
||||||
Q_snprintf( buf, sizeof( buf ), "%s/", fs_rodir );
|
Q_snprintf( buf, sizeof( buf ), "%s/", fs_rodir );
|
||||||
@@ -1567,15 +1597,62 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
|||||||
}
|
}
|
||||||
FS_AddGameDirectory( "./", FS_STATIC_PATH );
|
FS_AddGameDirectory( "./", FS_STATIC_PATH );
|
||||||
|
|
||||||
|
// but scan rodir for games first
|
||||||
|
if( COM_CheckStringEmpty( fs_rodir ))
|
||||||
|
{
|
||||||
|
stringlistinit( &dirs );
|
||||||
|
listdirectory( &dirs, fs_rodir, true );
|
||||||
|
stringlistsort( &dirs );
|
||||||
|
|
||||||
|
for( i = 0; i < dirs.numstrings; i++ )
|
||||||
|
{
|
||||||
|
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( FI.games[FI.numgames] == NULL )
|
||||||
|
FI.games[FI.numgames] = Mem_Malloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
|
||||||
|
|
||||||
|
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], true ))
|
||||||
|
FI.numgames++;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringlistfreecontents( &dirs );
|
||||||
|
}
|
||||||
|
|
||||||
|
rodir_num_games = FI.numgames;
|
||||||
|
|
||||||
|
stringlistinit( &dirs );
|
||||||
|
listdirectory( &dirs, "./", true );
|
||||||
|
stringlistsort( &dirs );
|
||||||
|
|
||||||
for( i = 0; i < dirs.numstrings; i++ )
|
for( i = 0; i < dirs.numstrings; i++ )
|
||||||
{
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
if( !FS_SysFolderExists( dirs.strings[i] ))
|
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( FI.games[FI.numgames] == NULL )
|
// update gameinfo from rwdir, if it's newer
|
||||||
FI.games[FI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t ));
|
// with rodir we should never create new gameinfos anymore,
|
||||||
|
// so this only catches possible user changes
|
||||||
|
for( j = 0; j < rodir_num_games; j++ )
|
||||||
|
{
|
||||||
|
if( !Q_stricmp( dirs.strings[i], FI.games[j]->gamefolder ))
|
||||||
|
{
|
||||||
|
gameinfo_t gi;
|
||||||
|
if( FS_ParseGameInfo( dirs.strings[i], &gi, false ))
|
||||||
|
{
|
||||||
|
if( gi.mtime > FI.games[j]->mtime )
|
||||||
|
*FI.games[j] = gi;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames] ))
|
if( FI.games[FI.numgames] == NULL )
|
||||||
|
FI.games[FI.numgames] = Mem_Calloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
|
||||||
|
|
||||||
|
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], false ))
|
||||||
FI.numgames++; // added
|
FI.numgames++; // added
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3187,6 +3264,8 @@ const fs_api_t g_api =
|
|||||||
FS_LoadFileFromArchive,
|
FS_LoadFileFromArchive,
|
||||||
|
|
||||||
FS_GetRootDirectory,
|
FS_GetRootDirectory,
|
||||||
|
|
||||||
|
FS_MakeGameInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );
|
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ typedef struct gameinfo_s
|
|||||||
qboolean animated_title;
|
qboolean animated_title;
|
||||||
|
|
||||||
char demomap[MAX_QPATH];
|
char demomap[MAX_QPATH];
|
||||||
|
|
||||||
|
qboolean rodir; // if true, parsed from rodir
|
||||||
|
int64_t mtime;
|
||||||
} gameinfo_t;
|
} gameinfo_t;
|
||||||
|
|
||||||
typedef struct fs_dllinfo_t
|
typedef struct fs_dllinfo_t
|
||||||
@@ -226,6 +229,8 @@ typedef struct fs_api_t
|
|||||||
|
|
||||||
// gets current root directory, set by InitStdio
|
// gets current root directory, set by InitStdio
|
||||||
qboolean (*GetRootDirectory)( char *path, size_t size );
|
qboolean (*GetRootDirectory)( char *path, size_t size );
|
||||||
|
|
||||||
|
void (*MakeGameInfo)( void );
|
||||||
} fs_api_t;
|
} fs_api_t;
|
||||||
|
|
||||||
typedef struct fs_interface_t
|
typedef struct fs_interface_t
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ void stringlistinit( stringlist_t *list );
|
|||||||
void stringlistfreecontents( stringlist_t *list );
|
void stringlistfreecontents( stringlist_t *list );
|
||||||
void stringlistappend( stringlist_t *list, const char *text );
|
void stringlistappend( stringlist_t *list, const char *text );
|
||||||
void stringlistsort( stringlist_t *list );
|
void stringlistsort( stringlist_t *list );
|
||||||
void listdirectory( stringlist_t *list, const char *path );
|
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only );
|
||||||
|
|
||||||
// filesystem ops
|
// filesystem ops
|
||||||
int FS_FileExists( const char *filename, int gamedironly );
|
int FS_FileExists( const char *filename, int gamedironly );
|
||||||
@@ -230,6 +230,7 @@ qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
|
|||||||
//
|
//
|
||||||
// pak.c
|
// pak.c
|
||||||
//
|
//
|
||||||
|
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files );
|
||||||
searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags );
|
searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags );
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error )
|
|||||||
|
|
||||||
// TODO: use FS_Open to allow PK3 to be included into other archives
|
// TODO: use FS_Open to allow PK3 to be included into other archives
|
||||||
// Currently, it doesn't work with rodir due to FS_FindFile logic
|
// Currently, it doesn't work with rodir due to FS_FindFile logic
|
||||||
|
// when it will use FS_Open, check that FS_CheckForQuakePak correctly
|
||||||
|
// detects Quake gamedirs in RoDir
|
||||||
packhandle = FS_SysOpen( packfile, "rb" );
|
packhandle = FS_SysOpen( packfile, "rb" );
|
||||||
|
|
||||||
if( packhandle == NULL )
|
if( packhandle == NULL )
|
||||||
@@ -364,3 +366,45 @@ searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags )
|
|||||||
|
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
FS_CheckForQuakePak
|
||||||
|
|
||||||
|
To generate fake gameinfo for Quake directory, we need to parse pak0.pak
|
||||||
|
and find progs.dat in it
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files )
|
||||||
|
{
|
||||||
|
qboolean is_quake = false;
|
||||||
|
pack_t *pak;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pak = FS_LoadPackPAK( pakfile, NULL );
|
||||||
|
if( !pak )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for( i = 0; i < num_files; i++ )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for( j = 0; j < pak->numfiles; j++ )
|
||||||
|
{
|
||||||
|
if( Q_strchr( pak->files[j].name, '/' ))
|
||||||
|
continue; // exclude subdirectories
|
||||||
|
|
||||||
|
if( !Q_stricmp( pak->files[j].name, files[i] ))
|
||||||
|
{
|
||||||
|
is_quake = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( is_quake )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mem_Free( pak );
|
||||||
|
return is_quake;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ MEMFD_CREATE_TEST = '''#define _GNU_SOURCE
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
int main(int argc, char **argv) { return memfd_create(argv[0], 0); }'''
|
int main(int argc, char **argv) { return memfd_create(argv[0], 0); }'''
|
||||||
|
|
||||||
|
DIRENT_D_TYPE_TEST = '''#define _GNU_SOURCE
|
||||||
|
#include <dirent.h>
|
||||||
|
int main(int argc, char **argv) { struct dirent entry; entry.d_type = DT_DIR; return 0; }
|
||||||
|
'''
|
||||||
|
|
||||||
def options(opt):
|
def options(opt):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -18,6 +23,9 @@ def configure(conf):
|
|||||||
if conf.check_cc(fragment=MEMFD_CREATE_TEST, msg='Checking for memfd_create', mandatory=False):
|
if conf.check_cc(fragment=MEMFD_CREATE_TEST, msg='Checking for memfd_create', mandatory=False):
|
||||||
conf.define('HAVE_MEMFD_CREATE', 1)
|
conf.define('HAVE_MEMFD_CREATE', 1)
|
||||||
|
|
||||||
|
if conf.check_cc(fragment=DIRENT_D_TYPE_TEST, msg='Checking for d_type field in struct dirent', mandatory=False):
|
||||||
|
conf.define('HAVE_DIRENT_D_TYPE', 1)
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
bld(name = 'filesystem_includes', export_includes = '.')
|
bld(name = 'filesystem_includes', export_includes = '.')
|
||||||
|
|
||||||
|
|||||||
1
wscript
1
wscript
@@ -395,6 +395,7 @@ def configure(conf):
|
|||||||
conf.env.ENABLE_XAR = conf.options.ENABLE_XAR
|
conf.env.ENABLE_XAR = conf.options.ENABLE_XAR
|
||||||
conf.env.ENABLE_FUZZER = conf.options.ENABLE_FUZZER
|
conf.env.ENABLE_FUZZER = conf.options.ENABLE_FUZZER
|
||||||
conf.env.DEDICATED = conf.options.DEDICATED
|
conf.env.DEDICATED = conf.options.DEDICATED
|
||||||
|
conf.env.SUPPORT_BSP2_FORMAT = conf.options.SUPPORT_BSP2_FORMAT
|
||||||
|
|
||||||
conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT)
|
conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT)
|
||||||
conf.define_cond('SUPPORT_HL25_EXTENDED_STRUCTS', conf.options.SUPPORT_HL25_EXTENDED_STRUCTS)
|
conf.define_cond('SUPPORT_HL25_EXTENDED_STRUCTS', conf.options.SUPPORT_HL25_EXTENDED_STRUCTS)
|
||||||
|
|||||||
Reference in New Issue
Block a user