diff --git a/Documentation/extensions/addon-folders.md b/Documentation/extensions/addon-folders.md new file mode 100644 index 00000000..0611c2d5 --- /dev/null +++ b/Documentation/extensions/addon-folders.md @@ -0,0 +1,16 @@ +# Addon folders in Xash3D FWGS + +Xash3D FWGS supports both GoldSource-style addon folders and has few own. Each directory can have it's own archives that will be mounted with lower priority than directory itself. + +Below is the mounts map, in order of precedence from least important to most important. + +|--------------------|------| +| Directory | Note | +|--------------------|------| +| `$game/downloaded` | Always added. Used to store server downloads.| +| `$game` | This is the game directory. | +| `$game/custom` | Always added. Used for user modifications content. | +| `$game_hd` | Added with `fs_mount_hd` set to non-zero value. Used for high definition content, similar to GoldSrc.. | +| `$game_addon` | Added with `fs_mount_addon` set to non-zero value. Used for user modifications content, similar to GoldSrc. | +| `$game_lv` | Added with `fs_mount_lv` set to non-zero value. Used for low-violence content, similar to GoldSrc. | +| `$game_$language` | Added with `fs_mount_l10n` set to non-zero value. Language is controlled with `ui_language` cvar or `-language` command line switch. Used for localization content, similar to GoldSrc. | diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 2ac23e79..ff62956e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3127,9 +3127,6 @@ static qboolean CL_ShouldRescanFilesystem( void ) } } - if( FBitSet( fs_mount_lv.flags|fs_mount_hd.flags|fs_mount_addon.flags|fs_mount_l10n.flags|ui_language.flags, FCVAR_CHANGED )) - retval = true; - return retval; } diff --git a/engine/common/common.h b/engine/common/common.h index dee756a2..38657f73 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -167,11 +167,6 @@ extern convar_t cl_filterstuffcmd; extern convar_t rcon_password; extern convar_t hpk_custom_file; extern convar_t con_gamemaps; -extern convar_t fs_mount_lv; -extern convar_t fs_mount_hd; -extern convar_t fs_mount_addon; -extern convar_t fs_mount_l10n; -extern convar_t ui_language; // historically used for UI, but now controls mounted localization directory #define Mod_AllowMaterials() ( host_allow_materials.value != 0.0f && !FBitSet( host.features, ENGINE_DISABLE_HDTEXTURES )) @@ -416,7 +411,8 @@ byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamediro byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT; void FS_Rescan_f( void ); -void FS_CheckConfig( void ); +void FS_LoadGameInfo( void ); +void FS_SaveVFSConfig( void ); // // cmd.c @@ -687,6 +683,8 @@ void pfnResetTutorMessageDecayData( void ); void Con_CompleteCommand( field_t *field ); void Cmd_AutoComplete( char *complete_string ); void Cmd_AutoCompleteClear( void ); +void Host_InitializeConfig( file_t *f, const char *config, const char *description ); +void Host_FinalizeConfig( file_t *f, const char *config ); // // custom.c diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index e54dbfa4..06cea9db 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1403,8 +1403,15 @@ static void Cmd_WriteOpenGLVariables( file_t *f ) Cvar_LookupVars( FCVAR_GLCONFIG, NULL, f, (setpair_t)Cmd_WriteOpenGLCvar ); } -#if !XASH_DEDICATED -static void Host_FinalizeConfig( file_t *f, const char *config ) +void Host_InitializeConfig( file_t *f, const char *config, const char *description ) +{ + FS_Printf( f, "//=======================================================================\n"); + FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); + FS_Printf( f, "//\t\t%s - %s\n", config, description ); + FS_Printf( f, "//=======================================================================\n" ); +} + +void Host_FinalizeConfig( file_t *f, const char *config ) { string backup, newcfg; @@ -1418,6 +1425,8 @@ static void Host_FinalizeConfig( file_t *f, const char *config ) FS_Rename( newcfg, config ); } + +#if !XASH_DEDICATED /* =============== Host_WriteConfig @@ -1438,10 +1447,7 @@ void Host_WriteConfig( void ) if( f ) { Con_Reportf( "%s()\n", __func__ ); - FS_Printf( f, "//=======================================================================\n"); - FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); - FS_Printf( f, "//\t\tconfig.cfg - archive of cvars\n" ); - FS_Printf( f, "//=======================================================================\n" ); + Host_InitializeConfig( f, "config.cfg", "archive of cvars" ); Key_WriteBindings( f ); Cvar_WriteVariables( f, FCVAR_ARCHIVE ); Info_WriteVars( f ); @@ -1465,6 +1471,8 @@ void Host_WriteConfig( void ) else Con_DPrintf( S_ERROR "Couldn't write config.cfg.\n" ); NET_SaveMasters(); + + FS_SaveVFSConfig(); } /* @@ -1486,10 +1494,7 @@ void GAME_EXPORT Host_WriteServerConfig( const char *name ) if(( f = FS_Open( newconfigfile, "w", false )) != NULL ) { - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); - FS_Printf( f, "//\t\tgame.cfg - multiplayer server temporare config\n" ); - FS_Printf( f, "//=======================================================================\n" ); + Host_InitializeConfig( f, "game.cfg", "multiplayer server temporary config" ); Cvar_WriteVariables( f, FCVAR_SERVER ); CSCR_WriteGameCVars( f, "settings.scr" ); @@ -1521,11 +1526,7 @@ void Host_WriteOpenGLConfig( void ) if( f ) { Con_Reportf( "%s()\n", __func__ ); - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); - FS_Printf( f, "//\t\t%s - archive of renderer implementation cvars\n", name ); - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "\n" ); + Host_InitializeConfig( f, name, "archive of renderer implementation cvars" ); Cmd_WriteOpenGLVariables( f ); Host_FinalizeConfig( f, name ); @@ -1551,10 +1552,7 @@ void Host_WriteVideoConfig( void ) if( f ) { Con_Reportf( "%s()\n", __func__ ); - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); - FS_Printf( f, "//\t\tvideo.cfg - archive of renderer variables\n"); - FS_Printf( f, "//=======================================================================\n" ); + Host_InitializeConfig( f, "video.cfg", "archive of renderer variables" ); Cvar_WriteVariables( f, FCVAR_RENDERINFO ); Host_FinalizeConfig( f, "video.cfg" ); } @@ -1577,11 +1575,7 @@ void Key_EnumCmds_f( void ) f = FS_Open( "../help.txt", "w", false ); if( f ) { - FS_Printf( f, "//=======================================================================\n"); - FS_Printf( f, "//\tGenerated by "XASH_ENGINE_NAME" (%i, %s, %s, %s-%s)\n", Q_buildnum(), g_buildcommit, g_buildbranch, Q_buildos(), Q_buildarch()); - FS_Printf( f, "//\t\thelp.txt - xash commands and console variables\n"); - FS_Printf( f, "//=======================================================================\n"); - + Host_InitializeConfig( f, "help.txt", "xash commands and console variables" ); FS_Printf( f, "\n\n\t\t\tconsole variables\n\n"); Cvar_LookupVars( 0, NULL, f, (setpair_t)Cmd_WriteHelp ); FS_Printf( f, "\n\n\t\t\tconsole commands\n\n"); diff --git a/engine/common/filesystem_engine.c b/engine/common/filesystem_engine.c index fb46014b..6001c3e8 100644 --- a/engine/common/filesystem_engine.c +++ b/engine/common/filesystem_engine.c @@ -27,11 +27,11 @@ GNU General Public License for more details. #include "library.h" #include "platform/platform.h" -CVAR_DEFINE_AUTO( fs_mount_hd, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount high definition content folder" ); -CVAR_DEFINE_AUTO( fs_mount_lv, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount low violence models content folder" ); -CVAR_DEFINE_AUTO( fs_mount_addon, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount addon content folder" ); -CVAR_DEFINE_AUTO( fs_mount_l10n, "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "mount localization content folder" ); -CVAR_DEFINE_AUTO( ui_language, "english", FCVAR_ARCHIVE|FCVAR_PRIVILEGED|FCVAR_LATCH, "selected game language" ); +static CVAR_DEFINE_AUTO( fs_mount_hd, "0", FCVAR_PRIVILEGED, "mount high definition content folder" ); +static CVAR_DEFINE_AUTO( fs_mount_lv, "0", FCVAR_PRIVILEGED, "mount low violence models content folder" ); +static CVAR_DEFINE_AUTO( fs_mount_addon, "0", FCVAR_PRIVILEGED, "mount addon content folder" ); +static CVAR_DEFINE_AUTO( fs_mount_l10n, "0", FCVAR_PRIVILEGED, "mount localization content folder" ); +static CVAR_DEFINE_AUTO( ui_language, "english", FCVAR_PRIVILEGED, "selected game language" ); fs_api_t g_fsapi; fs_globals_t *FI; @@ -81,7 +81,7 @@ void *FS_GetNativeObject( const char *obj ) return NULL; } -void FS_Rescan_f( void ) +static uint32_t FS_MountFlags( void ) { uint32_t flags = 0; @@ -91,15 +91,76 @@ void FS_Rescan_f( void ) if( fs_mount_addon.value ) SetBits( flags, FS_MOUNT_ADDON ); if( fs_mount_l10n.value ) SetBits( flags, FS_MOUNT_L10N ); - g_fsapi.Rescan( flags, ui_language.string ); + return flags; +} + +void FS_Rescan_f( void ) +{ + g_fsapi.Rescan( FS_MountFlags(), ui_language.string ); +} + +static void FS_LoadVFSConfig( const char *gamedir ) +{ + string parm; + + if( Host_IsDedicated( )) + return; + + Cbuf_AddTextf( "exec %s/vfs.cfg\n", gamedir ); + Cbuf_Execute(); + + if( Sys_GetParmFromCmdLine( "-language", parm )) + { + Cvar_DirectSet( &ui_language, parm ); + Cvar_DirectSet( &fs_mount_l10n, "1" ); + } - ClearBits( fs_mount_lv.flags, FCVAR_CHANGED ); ClearBits( fs_mount_hd.flags, FCVAR_CHANGED ); - ClearBits( fs_mount_addon.flags, FCVAR_CHANGED ); + ClearBits( fs_mount_lv.flags, FCVAR_CHANGED ); ClearBits( fs_mount_l10n.flags, FCVAR_CHANGED ); + ClearBits( fs_mount_addon.flags, FCVAR_CHANGED ); ClearBits( ui_language.flags, FCVAR_CHANGED ); } +void FS_SaveVFSConfig( void ) +{ + file_t *f; + + if( !FBitSet( fs_mount_hd.flags|fs_mount_lv.flags|fs_mount_l10n.flags|fs_mount_addon.flags|ui_language.flags, FCVAR_CHANGED )) + { + Con_Reportf( "%s: no need to save vfs.cfg\n", __func__ ); + return; + } + + f = FS_Open( "vfs.cfg.new", "w", true ); + if( !f ) + { + Con_Printf( S_ERROR "%s: couldn't open vfs.cfg for write\n", __func__ ); + return; + } + + FS_Printf( f, "%s \"%d\"\n", fs_mount_hd.name, (int)fs_mount_hd.value ); + FS_Printf( f, "%s \"%d\"\n", fs_mount_lv.name, (int)fs_mount_lv.value ); + FS_Printf( f, "%s \"%d\"\n", fs_mount_l10n.name, (int)fs_mount_l10n.value ); + FS_Printf( f, "%s \"%d\"\n", fs_mount_addon.name, (int)fs_mount_addon.value ); + FS_Printf( f, "%s \"%s\"\n", ui_language.name, ui_language.string ); + + Host_FinalizeConfig( f, "vfs.cfg" ); + + ClearBits( fs_mount_hd.flags, FCVAR_CHANGED ); + ClearBits( fs_mount_lv.flags, FCVAR_CHANGED ); + ClearBits( fs_mount_l10n.flags, FCVAR_CHANGED ); + ClearBits( fs_mount_addon.flags, FCVAR_CHANGED ); + ClearBits( ui_language.flags, FCVAR_CHANGED ); +} + +void FS_LoadGameInfo( void ) +{ + FS_LoadVFSConfig( g_fsapi.Gamedir( )); + + g_fsapi.LoadGameInfo( FS_MountFlags(), ui_language.string ); +} + static void FS_ClearPaths_f( void ) { FS_ClearSearchPath(); @@ -196,10 +257,7 @@ static qboolean FS_DetermineRootDirectory( char *out, size_t size ) return true; } -#if XASH_EMSCRIPTEN - Q_strncpy( out, "/rwdir", size ); - return true; -#elif TARGET_OS_IOS +#if TARGET_OS_IOS Q_strncpy( out, IOS_GetDocsDir(), size ); return true; #elif XASH_ANDROID && XASH_SDL @@ -264,20 +322,9 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size ) return true; } -#if XASH_EMSCRIPTEN - Q_strncpy( out, "/rodir", size ); - return true; -#endif - return false; } -void FS_CheckConfig( void ) -{ - if( fs_mount_lv.value || fs_mount_hd.value || fs_mount_addon.value || fs_mount_l10n.value ) - FS_Rescan_f(); -} - /* ================ FS_Init diff --git a/engine/common/host.c b/engine/common/host.c index 375c0729..b5ee1fe4 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -148,6 +148,7 @@ static void Sys_PrintUsage( const char *exename ) O("-bugcomp [opts] ", "enable precise bug compatibility") O(" ", "will break games that don't require it") O(" ", "refer to engine documentation for more info") + O("-language ", "mount localization game directory") O("-disablehelp ", "disable this message") #if !XASH_DEDICATED O("-dedicated ", "run engine in dedicated mode") @@ -1158,7 +1159,7 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole Host_RunTests( 1 ); #endif - FS_LoadGameInfo( NULL ); + FS_LoadGameInfo(); Cvar_PostFSInit(); Image_CheckPaletteQ1 (); @@ -1322,7 +1323,6 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa Cmd_RemoveCommand( "setgl" ); Cbuf_ExecStuffCmds(); // execute stuffcmds (commandline) SCR_CheckStartupVids(); // must be last - FS_CheckConfig(); if( Sys_GetParmFromCmdLine( "-timedemo", demoname )) Cbuf_AddTextf( "timedemo %s\n", demoname ); diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 417a6dd0..9d2048fa 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1368,6 +1368,21 @@ void FS_Rescan( uint32_t flags, const char *language ) FS_AddGameHierarchy( GI->gamefolder, FS_GAMEDIR_PATH | flags ); } +/* +=============== +FS_Gamedir + +Allows engine to know game directory before gameinfo is initialized +=============== +*/ +static const char *FS_Gamedir( void ) +{ + if( GI ) + return GI->gamefolder; + + return fs_gamedir; +} + /* ================ FS_LoadGameInfo @@ -1375,17 +1390,14 @@ FS_LoadGameInfo can be passed null arg ================ */ -void FS_LoadGameInfo( const char *rootfolder ) +static void FS_LoadGameInfo( uint32_t flags, const char *language ) { int i; // lock uplevel of gamedir for read\write FS_AllowDirectPaths( false ); - if( rootfolder ) - Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir )); - - Con_Reportf( "%s( %s )\n", __func__, fs_gamedir ); + Con_Reportf( "%s( %s, 0x%x, %s )\n", __func__, fs_gamedir, flags, language ); // clear any old paths FS_ClearSearchPath(); @@ -1410,7 +1422,7 @@ void FS_LoadGameInfo( const char *rootfolder ) FS_CreatePath( buf ); } - FS_Rescan( 0, NULL ); // create new filesystem + FS_Rescan( flags, language ); // create new filesystem } /* @@ -3461,6 +3473,7 @@ const fs_api_t g_api = FS_Path_f, // gameinfo utils + FS_Gamedir, FS_LoadGameInfo, // file ops diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h index 2de28919..46621cc6 100644 --- a/filesystem/filesystem.h +++ b/filesystem/filesystem.h @@ -32,8 +32,8 @@ extern "C" { #endif // __cplusplus -#define FS_API_VERSION 3 // not stable yet! -#define FS_API_CREATEINTERFACE_TAG "XashFileSystem002" // follow FS_API_VERSION!!! +#define FS_API_VERSION 4 // not stable yet! +#define FS_API_CREATEINTERFACE_TAG "XashFileSystem004" // follow FS_API_VERSION!!! #define FILESYSTEM_INTERFACE_VERSION "VFileSystem009" // never change this! // search path flags @@ -167,7 +167,8 @@ typedef struct fs_api_t void (*Path_f)( void ); // gameinfo utils - void (*LoadGameInfo)( const char *rootfolder ); + const char *(*Gamedir)( void ); + void (*LoadGameInfo)( uint32_t flags, const char *language ); // file ops file_t *(*Open)( const char *filepath, const char *mode, qboolean gamedironly ); diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index ed7c241e..39023dfe 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -166,9 +166,6 @@ int FS_SetCurrentDirectory( const char *path ); qboolean FS_GetRootDirectory( char *path, size_t size ); void FS_Path_f( void ); -// gameinfo utils -void FS_LoadGameInfo( const char *rootfolder ); - // file ops int FS_Close( file_t *file ); file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) diff --git a/filesystem/fscallback.h b/filesystem/fscallback.h index 6fb70e5c..d33d9e11 100644 --- a/filesystem/fscallback.h +++ b/filesystem/fscallback.h @@ -42,9 +42,6 @@ extern fs_globals_t *FI; #define FS_SetCurrentDirectory (*g_fsapi.SetCurrentDirectory) #define FS_Path_f (*g_fsapi.Path_f) -// gameinfo utils -#define FS_LoadGameInfo (*g_fsapi.LoadGameInfo) - // file ops #ifndef FSCALLBACK_OVERRIDE_MALLOC_LIKE #define FS_Open (*g_fsapi.Open)