From 0ebf1df74238e91a514e5fd99d7d98672e4f80ec Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Mon, 1 Sep 2025 20:00:14 +0100 Subject: [PATCH] XDG home directory support --- README.md | 32 ++- code/botlib/l_log.c | 2 +- code/client/cl_avi.c | 6 +- code/client/cl_console.c | 2 +- code/client/cl_keys.c | 2 +- code/client/cl_main.c | 18 +- code/client/cl_parse.c | 4 +- code/client/cl_ui.c | 2 +- code/qcommon/common.c | 12 +- code/qcommon/files.c | 144 +++++++++----- code/qcommon/q_shared.h | 10 +- code/qcommon/qcommon.h | 20 +- code/renderergl1/tr_init.c | 2 +- code/renderergl2/tr_init.c | 2 +- code/server/sv_ccmds.c | 2 +- code/sys/sys_main.c | 6 +- code/sys/sys_unix.c | 396 ++++++++++++++++++++++++++++++++----- code/sys/sys_win32.c | 8 +- 18 files changed, 527 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index c5f87786..79669cf6 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,31 @@ set using command line arguments: ioquake3 +set cl_renderer opengl2 +set r_preferOpenGLES 1 +# Filesystem + +Compared to the original release, user configuration and data files are stored +in more modern locations. If you want a different behaviour a specific path +can be provided by adding `+set fs_homepath ` to the command line. + +### Windows + +`C:\Users\\AppData\Roaming\Quake3` + +### macOS + +`/Users//Library/Application Support/Quake3` + +### Linux + +`/home//.config/Quake3` Configuration files. +`/home//.local/share/Quake3` Data files (pk3s etc.). +`/home//.local/state/Quake3` Other internal runtime files. + +These directories correspond to the Free Desktop XDG Base Directory +Specification. The original release used `/home/.q3a`. This will be used if +present, however in this case a prompt will be shown suggesting migration to +the above locations, if desired. + # Console ## New cvars @@ -480,9 +505,8 @@ binary must not detect any original quake3 game pak files. If this condition is met, the game will set com_standalone to 1 and is then running in stand alone mode. -If you want the engine to use a different directory in your homepath than -e.g. "Quake3" on Windows or ".q3a" on Linux, then set a new name at startup -by adding +If you want the engine to use a different directory in your homepaths than +"Quake3" then set a new name at startup by adding +set com_homepath @@ -496,7 +520,7 @@ matching game name. Example line: - +set com_basegame basefoo +set com_homepath .foo + +set com_basegame basefoo +set com_homepath foo +set com_gamename foo If you really changed parts that would make vanilla ioquake3 incompatible with diff --git a/code/botlib/l_log.c b/code/botlib/l_log.c index 4d7c4e89..d6a91c77 100644 --- a/code/botlib/l_log.c +++ b/code/botlib/l_log.c @@ -71,7 +71,7 @@ void Log_Open(char *filename) botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); return; } //end if - ospath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), Cvar_VariableString("fs_game"), filename); + ospath = FS_BuildOSPath(Cvar_VariableString("fs_homestatepath"), Cvar_VariableString("fs_game"), filename); logfile.fp = fopen(ospath, "wb"); if (!logfile.fp) { diff --git a/code/client/cl_avi.c b/code/client/cl_avi.c index ef5456c1..a403d6f5 100644 --- a/code/client/cl_avi.c +++ b/code/client/cl_avi.c @@ -335,10 +335,10 @@ qboolean CL_OpenAVIForWriting( const char *fileName ) return qfalse; } - if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 ) + if( ( afd.f = FS_FOpenFileWrite_HomeData( fileName ) ) <= 0 ) return qfalse; - if( ( afd.idxF = FS_FOpenFileWrite( + if( ( afd.idxF = FS_FOpenFileWrite_HomeData( va( "%s" INDEX_FILE_EXTENSION, fileName ) ) ) <= 0 ) { FS_FCloseFile( afd.f ); @@ -635,7 +635,7 @@ qboolean CL_CloseAVI( void ) FS_FCloseFile( afd.idxF ); // Remove temp index file - FS_HomeRemove( idxFileName ); + FS_Remove_HomeData( idxFileName ); // Write the real header FS_Seek( afd.f, 0, FS_SEEK_SET ); diff --git a/code/client/cl_console.c b/code/client/cl_console.c index 1748e047..0be0c118 100644 --- a/code/client/cl_console.c +++ b/code/client/cl_console.c @@ -201,7 +201,7 @@ void Con_Dump_f (void) return; } - f = FS_FOpenFileWrite( filename ); + f = FS_FOpenFileWrite_HomeData( filename ); if (!f) { Com_Printf ("ERROR: couldn't open %s.\n", filename); diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index ef134fc7..d9918033 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -1577,7 +1577,7 @@ void CL_SaveConsoleHistory( void ) consoleSaveBufferSize = strlen( consoleSaveBuffer ); - f = FS_FOpenFileWrite( CONSOLE_HISTORY_FILE ); + f = FS_FOpenFileWrite_HomeState( CONSOLE_HISTORY_FILE ); if( !f ) { Com_Printf( "Couldn't write %s.\n", CONSOLE_HISTORY_FILE ); diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 7cb92f6c..d6c530a5 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -742,7 +742,7 @@ void CL_Record_f( void ) { #endif Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer); - if (!FS_FileExists(name)) + if (!FS_FileExists_HomeData(name)) break; // file doesn't exist } } @@ -750,7 +750,7 @@ void CL_Record_f( void ) { // open the demo file Com_Printf ("recording to %s.\n", name); - clc.demofile = FS_FOpenFileWrite( name ); + clc.demofile = FS_FOpenFileWrite_HomeData( name ); if ( !clc.demofile ) { Com_Printf ("ERROR: couldn't open.\n"); return; @@ -903,7 +903,7 @@ void CL_DemoCompleted( void ) else numFrames = clc.timeDemoFrames - 1; - f = FS_FOpenFileWrite( cl_timedemoLog->string ); + f = FS_FOpenFileWrite_HomeData( cl_timedemoLog->string ); if( f ) { FS_Printf( f, "# %s", buffer ); @@ -2197,7 +2197,7 @@ static void CL_BeginHttpDownload( const char *remoteURL ) { CL_HTTP_BeginDownload(remoteURL); Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL)); - clc.download = FS_BaseDir_FOpenFileWrite(clc.downloadTempName); + clc.download = FS_BaseDir_FOpenFileWrite_HomeData(clc.downloadTempName); if(!clc.download) { Com_Error(ERR_DROP, "CL_BeginHTTPDownload: failed to open " "%s for writing", clc.downloadTempName); @@ -2233,7 +2233,7 @@ void CL_NextDownload(void) // A download has finished, check whether this matches a referenced checksum if(*clc.downloadName) { - char *zippath = FS_BaseDir_BuildOSPath(Cvar_VariableString("fs_homepath"), clc.downloadName); + char *zippath = FS_BaseDir_BuildOSPath(Cvar_VariableString("fs_homedatapath"), clc.downloadName); if(!FS_CompareZipChecksum(zippath)) Com_Error(ERR_DROP, "Incorrect checksum for file: %s", clc.downloadName); @@ -2977,7 +2977,7 @@ void CL_Frame ( int msec ) { clc.download = 0; } - FS_BaseDir_Rename(clc.downloadTempName, clc.downloadName, qfalse); + FS_BaseDir_Rename_HomeData(clc.downloadTempName, clc.downloadName, qfalse); clc.downloadRestart = qtrue; CL_NextDownload(); } @@ -3307,7 +3307,7 @@ void CL_InitRef( void ) { ri.FS_FreeFileList = FS_FreeFileList; ri.FS_ListFiles = FS_ListFiles; ri.FS_FileIsInPAK = FS_FileIsInPAK; - ri.FS_FileExists = FS_FileExists; + ri.FS_FileExists = FS_FileExists_HomeData; ri.Cvar_Get = Cvar_Get; ri.Cvar_Set = Cvar_Set; ri.Cvar_SetValue = Cvar_SetValue; @@ -3418,7 +3418,7 @@ void CL_Video_f( void ) Com_sprintf( filename, MAX_OSPATH, "videos/video%d%d%d%d.avi", a, b, c, d ); - if( !FS_FileExists( filename ) ) + if( !FS_FileExists_HomeData( filename ) ) break; // file doesn't exist } @@ -3471,7 +3471,7 @@ static void CL_GenerateQKey(void) Com_Printf( "QKEY building random string\n" ); Com_RandomBytes( buff, sizeof(buff) ); - f = FS_BaseDir_FOpenFileWrite( QKEY_FILE ); + f = FS_BaseDir_FOpenFileWrite_HomeState( QKEY_FILE ); if( !f ) { Com_Printf( "QKEY could not open %s for write\n", QKEY_FILE ); diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 82da5c37..d8b5e2a5 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -608,7 +608,7 @@ void CL_ParseDownload ( msg_t *msg ) { // open the file if not opened yet if (!clc.download) { - clc.download = FS_BaseDir_FOpenFileWrite( clc.downloadTempName ); + clc.download = FS_BaseDir_FOpenFileWrite_HomeData( clc.downloadTempName ); if (!clc.download) { Com_Printf( "Could not create %s\n", clc.downloadTempName ); @@ -635,7 +635,7 @@ void CL_ParseDownload ( msg_t *msg ) { clc.download = 0; // rename the file - FS_BaseDir_Rename ( clc.downloadTempName, clc.downloadName, qfalse ); + FS_BaseDir_Rename_HomeData ( clc.downloadTempName, clc.downloadName, qfalse ); } // send intentions now diff --git a/code/client/cl_ui.c b/code/client/cl_ui.c index dfc5ad16..df9a5c4a 100644 --- a/code/client/cl_ui.c +++ b/code/client/cl_ui.c @@ -74,7 +74,7 @@ LAN_SaveServersToCache */ void LAN_SaveServersToCache( void ) { int size; - fileHandle_t fileOut = FS_BaseDir_FOpenFileWrite("servercache.dat"); + fileHandle_t fileOut = FS_BaseDir_FOpenFileWrite_HomeState("servercache.dat"); FS_Write(&cls.numglobalservers, sizeof(int), fileOut); FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut); size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers); diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 9e6bf597..22ee4270 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -205,7 +205,7 @@ void QDECL Com_Printf( const char *fmt, ... ) { time( &aclock ); newtime = localtime( &aclock ); - logfile = FS_FOpenFileWrite( "qconsole.log" ); + logfile = FS_FOpenFileWrite_HomeData( "qconsole.log" ); if(logfile) { @@ -1921,8 +1921,8 @@ void Com_InitJournaling( void ) { if ( com_journal->integer == 1 ) { Com_Printf( "Journaling events\n"); - com_journalFile = FS_FOpenFileWrite( "journal.dat" ); - com_journalDataFile = FS_FOpenFileWrite( "journaldata.dat" ); + com_journalFile = FS_FOpenFileWrite_HomeState( "journal.dat" ); + com_journalDataFile = FS_FOpenFileWrite_HomeState( "journaldata.dat" ); } else if ( com_journal->integer == 2 ) { Com_Printf( "Replaying journaled events\n"); FS_FOpenFileRead( "journal.dat", &com_journalFile, qtrue ); @@ -2547,7 +2547,7 @@ static void Com_WriteCDKey( const char *filename, const char *ikey ) { #ifndef _WIN32 savedumask = umask(0077); #endif - f = FS_BaseDir_FOpenFileWrite( fbuffer ); + f = FS_BaseDir_FOpenFileWrite_HomeState( fbuffer ); if ( !f ) { Com_Printf ("Couldn't write CD key to %s.\n", fbuffer ); goto out; @@ -2921,7 +2921,7 @@ void Com_ReadFromPipe( void ) void Com_WriteConfigToFile( const char *filename ) { fileHandle_t f; - f = FS_FOpenFileWrite( filename ); + f = FS_FOpenFileWrite_HomeConfig( filename ); if ( !f ) { Com_Printf ("Couldn't write %s.\n", filename ); return; @@ -3293,7 +3293,7 @@ void Com_Shutdown (void) { if( pipefile ) { FS_FCloseFile( pipefile ); - FS_HomeRemove( com_pipefile->string ); + FS_Remove_HomeData( com_pipefile->string ); } } diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 5326bfd7..0e59f8df 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -246,7 +246,9 @@ typedef struct searchpath_s { static char fs_gamedir[MAX_OSPATH]; // this will be a single file name with no separators static cvar_t *fs_debug; -static cvar_t *fs_homepath; +static cvar_t *fs_homeconfigpath; +static cvar_t *fs_homedatapath; +static cvar_t *fs_homestatepath; static cvar_t *fs_apppath; static cvar_t *fs_steampath; @@ -609,14 +611,14 @@ void FS_Remove( const char *osPath ) { /* =========== -FS_HomeRemove +FS_Remove_HomeData =========== */ -void FS_HomeRemove( const char *homePath ) { +void FS_Remove_HomeData( const char *homePath ) { FS_CheckFilenameIsMutable( homePath, __func__ ); - remove( FS_BuildOSPath( fs_homepath->string, + remove( FS_BuildOSPath( fs_homedatapath->string, fs_gamedir, homePath ) ); } @@ -644,7 +646,7 @@ qboolean FS_FileInPathExists(const char *testpath) /* ================ -FS_FileExists +FS_FileExists_HomeData Tests if the file exists in the current gamedir, this DOES NOT search the paths. This is to determine if opening a file to write @@ -652,21 +654,21 @@ search the paths. This is to determine if opening a file to write NOTE TTimo: this goes with FS_FOpenFileWrite for opening the file afterwards ================ */ -qboolean FS_FileExists(const char *file) +qboolean FS_FileExists_HomeData(const char *file) { - return FS_FileInPathExists(FS_BuildOSPath(fs_homepath->string, fs_gamedir, file)); + return FS_FileInPathExists(FS_BuildOSPath(fs_homedatapath->string, fs_gamedir, file)); } /* ================ -FS_BaseDir_FileExists +FS_BaseDir_FileExists_HomeData Tests if the file exists ================ */ -qboolean FS_BaseDir_FileExists( const char *file ) +static qboolean FS_BaseDir_FileExists_HomeData( const char *file ) { - return FS_FileInPathExists(FS_BaseDir_BuildOSPath(fs_homepath->string, file)); + return FS_FileInPathExists(FS_BaseDir_BuildOSPath(fs_homedatapath->string, file)); } @@ -708,13 +710,32 @@ static fileHandle_t FS_OSPath_FOpenFileWrite( const char *ospath, const char *fi /* =========== -FS_BaseDir_FOpenFileWrite - +FS_BaseDir_FOpenFileWrite_HomeConfig =========== */ -fileHandle_t FS_BaseDir_FOpenFileWrite( const char *filename ) { +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeConfig( const char *filename ) { return FS_OSPath_FOpenFileWrite( - FS_BaseDir_BuildOSPath(fs_homepath->string, filename), filename); + FS_BaseDir_BuildOSPath(fs_homeconfigpath->string, filename), filename); +} + +/* +=========== +FS_BaseDir_FOpenFileWrite_HomeData +=========== +*/ +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeData( const char *filename ) { + return FS_OSPath_FOpenFileWrite( + FS_BaseDir_BuildOSPath(fs_homedatapath->string, filename), filename); +} + +/* +=========== +FS_BaseDir_FOpenFileWrite_HomeState +=========== +*/ +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeState( const char *filename ) { + return FS_OSPath_FOpenFileWrite( + FS_BaseDir_BuildOSPath(fs_homestatepath->string, filename), filename); } /* @@ -776,11 +797,11 @@ long FS_BaseDir_FOpenFileRead(const char *filename, fileHandle_t *fp) /* =========== -FS_BaseDir_Rename +FS_BaseDir_Rename_HomeData =========== */ -void FS_BaseDir_Rename( const char *from, const char *to, qboolean safe ) { +void FS_BaseDir_Rename_HomeData( const char *from, const char *to, qboolean safe ) { char *from_ospath, *to_ospath; if ( !fs_searchpaths ) { @@ -790,11 +811,11 @@ void FS_BaseDir_Rename( const char *from, const char *to, qboolean safe ) { // don't let sound stutter S_ClearSoundBuffer(); - from_ospath = FS_BaseDir_BuildOSPath( fs_homepath->string, from ); - to_ospath = FS_BaseDir_BuildOSPath( fs_homepath->string, to ); + from_ospath = FS_BaseDir_BuildOSPath( fs_homedatapath->string, from ); + to_ospath = FS_BaseDir_BuildOSPath( fs_homedatapath->string, to ); if ( fs_debug->integer ) { - Com_Printf( "FS_BaseDir_Rename: %s --> %s\n", from_ospath, to_ospath ); + Com_Printf( "FS_BaseDir_Rename_HomeData: %s --> %s\n", from_ospath, to_ospath ); } if ( safe ) { @@ -837,22 +858,41 @@ void FS_FCloseFile( fileHandle_t f ) { /* =========== -FS_FOpenFileWrite - +FS_FOpenFileWrite_HomeConfig =========== */ -fileHandle_t FS_FOpenFileWrite( const char *filename ) { +fileHandle_t FS_FOpenFileWrite_HomeConfig( const char *filename ) { return FS_OSPath_FOpenFileWrite( - FS_BuildOSPath(fs_homepath->string, fs_gamedir, filename), filename); + FS_BuildOSPath(fs_homeconfigpath->string, fs_gamedir, filename), filename); } /* =========== -FS_FOpenFileAppend +FS_FOpenFileWrite_HomeData +=========== +*/ +fileHandle_t FS_FOpenFileWrite_HomeData( const char *filename ) { + return FS_OSPath_FOpenFileWrite( + FS_BuildOSPath(fs_homedatapath->string, fs_gamedir, filename), filename); +} + +/* +=========== +FS_FOpenFileWrite_HomeState +=========== +*/ +fileHandle_t FS_FOpenFileWrite_HomeState( const char *filename ) { + return FS_OSPath_FOpenFileWrite( + FS_BuildOSPath(fs_homestatepath->string, fs_gamedir, filename), filename); +} + +/* +=========== +FS_FOpenFileAppend_HomeData =========== */ -fileHandle_t FS_FOpenFileAppend( const char *filename ) { +fileHandle_t FS_FOpenFileAppend_HomeData( const char *filename ) { char *ospath; fileHandle_t f; @@ -868,10 +908,10 @@ fileHandle_t FS_FOpenFileAppend( const char *filename ) { // don't let sound stutter S_ClearSoundBuffer(); - ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homedatapath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { - Com_Printf( "FS_FOpenFileAppend: %s\n", ospath ); + Com_Printf( "FS_FOpenFileAppend_HomeData: %s\n", ospath ); } FS_CheckFilenameIsMutable( ospath, __func__ ); @@ -911,7 +951,7 @@ fileHandle_t FS_FCreateOpenPipeFile( const char *filename ) { // don't let sound stutter S_ClearSoundBuffer(); - ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homedatapath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FCreateOpenPipeFile: %s\n", ospath ); @@ -1237,7 +1277,7 @@ long FS_FOpenFileReadDir(const char *filename, searchpath_t *search, fileHandle_ // if you are using FS_ReadFile to find out if a file exists, // this test can make the search fail although the file is in the directory // I had the problem on https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=8 - // turned out I used FS_FileExists instead + // turned out I used FS_FileExists_HomeData instead if(!unpure && fs_numServerPaks) { if(!FS_IsExt(filename, ".cfg", len) && // for config files @@ -1909,7 +1949,7 @@ void FS_WriteFile( const char *qpath, const void *buffer, int size ) { Com_Error( ERR_FATAL, "FS_WriteFile: NULL parameter" ); } - f = FS_FOpenFileWrite( qpath ); + f = FS_FOpenFileWrite_HomeData( qpath ); if ( !f ) { Com_Printf( "Failed to open %s\n", qpath ); return; @@ -3132,7 +3172,7 @@ qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ) { // Local name Q_strcat( neededpaks, len, "@"); // Do we have one with the same name? - if ( FS_BaseDir_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) + if ( FS_BaseDir_FileExists_HomeData( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) { char st[MAX_ZPATH]; // We already have one called this, we need to download it to another name @@ -3159,7 +3199,7 @@ qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ) { Q_strcat( neededpaks, len, fs_serverReferencedPakNames[i] ); Q_strcat( neededpaks, len, ".pk3" ); // Do we have one with the same name? - if ( FS_BaseDir_FileExists( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) + if ( FS_BaseDir_FileExists_HomeData( va( "%s.pk3", fs_serverReferencedPakNames[i] ) ) ) { Q_strcat( neededpaks, len, " (local file exists with wrong checksum)"); } @@ -3302,7 +3342,9 @@ FS_InitPathVars static void FS_InitPathVars( void ) { memset( fs_pathVars, 0, sizeof( fs_pathVars ) ); - FS_AddPathVar( fs_homepath ); + FS_AddPathVar( fs_homeconfigpath ); + FS_AddPathVar( fs_homedatapath ); + FS_AddPathVar( fs_homestatepath ); FS_AddPathVar( fs_basepath ); FS_AddPathVar( fs_apppath ); FS_AddPathVar( fs_steampath ); @@ -3317,7 +3359,18 @@ FS_Startup */ static void FS_Startup( const char *gameName ) { - const char *homePath; + cvar_t *fs_homepath = Cvar_Get("fs_homepath", "", CVAR_INIT|CVAR_PROTECTED); + const char *configPath = Sys_DefaultHomeConfigPath(); + const char *dataPath = Sys_DefaultHomeDataPath(); + const char *statePath = Sys_DefaultHomeStatePath(); + + if(*(fs_homepath)->string) { + // Setting fs_homepath manually overrides everything else + configPath = dataPath = statePath = fs_homepath->string; + } else if(!*configPath || !*dataPath || !*statePath) { + // #shouldneverhappen; just a sensible fallback + configPath = dataPath = statePath = Sys_DefaultInstallPath(); + } Com_Printf( "----- FS_Startup -----\n" ); @@ -3326,11 +3379,9 @@ static void FS_Startup( const char *gameName ) fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT|CVAR_PROTECTED ); fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); - homePath = Sys_DefaultHomePath(); - if (!homePath || !homePath[0]) { - homePath = fs_basepath->string; - } - fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT|CVAR_PROTECTED ); + fs_homeconfigpath = Cvar_Get ("fs_homeconfigpath", configPath, CVAR_INIT|CVAR_PROTECTED ); + fs_homedatapath = Cvar_Get ("fs_homedatapath", dataPath, CVAR_INIT|CVAR_PROTECTED ); + fs_homestatepath = Cvar_Get ("fs_homestatepath", statePath, CVAR_INIT|CVAR_PROTECTED ); fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); fs_steampath = Cvar_Get ("fs_steampath", Sys_SteamPath(), CVAR_INIT|CVAR_PROTECTED ); fs_gogpath = Cvar_Get ("fs_gogpath", Sys_GogPath(), CVAR_INIT|CVAR_PROTECTED ); @@ -3363,9 +3414,9 @@ static void FS_Startup( const char *gameName ) Com_Error( ERR_DROP, "Invalid fs_game '%s'", fs_gamedirvar->string ); } - if (fs_homepath->string[0] && Q_stricmp(fs_homepath->string, fs_basepath->string)) { - FS_CreatePath ( fs_homepath->string ); - } + FS_CreatePath(fs_homeconfigpath->string); + FS_CreatePath(fs_homedatapath->string); + FS_CreatePath(fs_homestatepath->string); FS_AddGameDirectories(gameName); @@ -3639,7 +3690,7 @@ static void FS_CheckPak0( void ) #endif if(installHome) - installPath = fs_homepath->string; + installPath = fs_homedatapath->string; else installPath = fs_basepath->string; @@ -4059,6 +4110,9 @@ void FS_InitFilesystem( void ) { // has already been initialized Com_StartupVariable("fs_basepath"); Com_StartupVariable("fs_homepath"); + Com_StartupVariable("fs_homeconfigpath"); + Com_StartupVariable("fs_homedatapath"); + Com_StartupVariable("fs_homestatepath"); Com_StartupVariable("fs_game"); if(!FS_FilenameCompare(Cvar_VariableString("fs_game"), com_basegame->string)) @@ -4201,7 +4255,7 @@ int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ) { r = FS_FOpenFileRead( qpath, f, qtrue ); break; case FS_WRITE: - *f = FS_FOpenFileWrite( qpath ); + *f = FS_FOpenFileWrite_HomeData( qpath ); r = 0; if (*f == 0) { r = -1; @@ -4210,7 +4264,7 @@ int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode ) { case FS_APPEND_SYNC: sync = qtrue; case FS_APPEND: - *f = FS_FOpenFileAppend( qpath ); + *f = FS_FOpenFileAppend_HomeData( qpath ); r = 0; if (*f == 0) { r = -1; diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index b3d749a0..1877a42c 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -31,9 +31,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define BASEGAME "foobar" #define CLIENT_WINDOW_TITLE "changeme" #define CLIENT_WINDOW_MIN_TITLE "changeme2" - #define HOMEPATH_NAME_UNIX ".foo" - #define HOMEPATH_NAME_WIN "FooBar" - #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN + #define HOMEPATH_NAME_UNIX_LEGACY ".foo" + #define HOMEPATH_NAME "FooBar" #define GAMENAME_FOR_MASTER "foobar" // must NOT contain whitespace #define CINEMATICS_LOGO "foologo.roq" #define CINEMATICS_INTRO "intro.roq" @@ -45,9 +44,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define BASEGAME "baseq3" #define CLIENT_WINDOW_TITLE "ioquake3" #define CLIENT_WINDOW_MIN_TITLE "ioq3" - #define HOMEPATH_NAME_UNIX ".q3a" - #define HOMEPATH_NAME_WIN "Quake3" - #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN + #define HOMEPATH_NAME_UNIX_LEGACY ".q3a" + #define HOMEPATH_NAME "Quake3" #define GAMENAME_FOR_MASTER "Quake3Arena" #define CINEMATICS_LOGO "idlogo.RoQ" #define CINEMATICS_INTRO "intro.RoQ" diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index d7e26459..1d9b61a9 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -616,7 +616,7 @@ char **FS_ListFiles( const char *directory, const char *extension, int *numfiles void FS_FreeFileList( char **list ); -qboolean FS_FileExists( const char *file ); +qboolean FS_FileExists_HomeData( const char *file ); qboolean FS_CreatePath (const char *OSPath); @@ -633,14 +633,18 @@ int FS_GetModList( char *listbuf, int bufsize ); void FS_GetModDescription( const char *modDir, char *description, int descriptionLen ); -fileHandle_t FS_FOpenFileWrite( const char *qpath ); -fileHandle_t FS_FOpenFileAppend( const char *filename ); +fileHandle_t FS_FOpenFileWrite_HomeConfig( const char *filename ); +fileHandle_t FS_FOpenFileWrite_HomeData( const char *filename ); +fileHandle_t FS_FOpenFileWrite_HomeState( const char *filename ); +fileHandle_t FS_FOpenFileAppend_HomeData( const char *filename ); fileHandle_t FS_FCreateOpenPipeFile( const char *filename ); // will properly create any needed paths and deal with seperater character issues -fileHandle_t FS_BaseDir_FOpenFileWrite( const char *filename ); +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeConfig( const char *filename ); +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeData( const char *filename ); +fileHandle_t FS_BaseDir_FOpenFileWrite_HomeState( const char *filename ); long FS_BaseDir_FOpenFileRead( const char *filename, fileHandle_t *fp ); -void FS_BaseDir_Rename( const char *from, const char *to, qboolean safe ); +void FS_BaseDir_Rename_HomeData( const char *from, const char *to, qboolean safe ); long FS_FOpenFileRead( const char *qpath, fileHandle_t *file, qboolean uniqueFILE ); // if uniqueFILE is true, then a new FILE will be fopened even if the file // is found in an already open pak file. If uniqueFILE is false, you must call @@ -725,7 +729,7 @@ qboolean FS_idPak(char *pak, char *base, int numPaks); qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); void FS_Remove( const char *osPath ); -void FS_HomeRemove( const char *homePath ); +void FS_Remove_HomeData( const char *homePath ); void FS_FilenameCompletion( const char *dir, const char *ext, char *filter, qboolean stripExt, void(*callback)(const char *s), qboolean allowNonPureFilesOnDisk ); @@ -1122,7 +1126,9 @@ char *Sys_MicrosoftStorePath(void); char *Sys_DefaultAppPath(void); #endif -char *Sys_DefaultHomePath(void); +char *Sys_DefaultHomeConfigPath(void); +char *Sys_DefaultHomeDataPath(void); +char *Sys_DefaultHomeStatePath(void); const char *Sys_Dirname( char *path ); const char *Sys_Basename( char *path ); char *Sys_ConsoleInput(void); diff --git a/code/renderergl1/tr_init.c b/code/renderergl1/tr_init.c index 3bf5d2b7..b2bc990d 100644 --- a/code/renderergl1/tr_init.c +++ b/code/renderergl1/tr_init.c @@ -333,7 +333,7 @@ static void R_ModeList_f( void ) NOTE TTimo some thoughts about the screenshots system: -screenshots get written in fs_homepath + fs_gamedir +screenshots get written in fs_homedatapath + fs_gamedir vanilla q3 .. baseq3/screenshots/ *.tga team arena .. missionpack/screenshots/ *.tga diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 6e4ea1c0..5389d09f 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -429,7 +429,7 @@ static void R_ModeList_f( void ) NOTE TTimo some thoughts about the screenshots system: -screenshots get written in fs_homepath + fs_gamedir +screenshots get written in fs_homedatapath + fs_gamedir vanilla q3 .. baseq3/screenshots/ *.tga team arena .. missionpack/screenshots/ *.tga diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index 1ef5db6f..6ae530cd 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -725,7 +725,7 @@ static void SV_WriteBans(void) Com_sprintf(filepath, sizeof(filepath), "%s/%s", FS_GetCurrentGameDir(), sv_banFile->string); - if((writeto = FS_BaseDir_FOpenFileWrite(filepath))) + if((writeto = FS_BaseDir_FOpenFileWrite_HomeState(filepath))) { char writebuf[128]; serverBan_t *curban; diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 0becb838..d7f53983 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -181,10 +181,10 @@ Sys_PIDFileName */ static char *Sys_PIDFileName( const char *gamedir ) { - const char *homePath = Cvar_VariableString( "fs_homepath" ); + const char *homeStatePath = Cvar_VariableString( "fs_homestatepath" ); - if( *homePath != '\0' ) - return va( "%s/%s/%s", homePath, gamedir, PID_FILENAME ); + if( *homeStatePath != '\0' ) + return va( "%s/%s/%s", homeStatePath, gamedir, PID_FILENAME ); return NULL; } diff --git a/code/sys/sys_unix.c b/code/sys/sys_unix.c index be054e37..5ec7d583 100644 --- a/code/sys/sys_unix.c +++ b/code/sys/sys_unix.c @@ -112,73 +112,371 @@ static int Sys_Exec( void ) } } +#ifdef __APPLE__ + /* ================== Sys_DefaultHomePath ================== */ -char *Sys_DefaultHomePath(void) +static char *Sys_DefaultHomePath(void) { static char homePath[ MAX_OSPATH ] = { 0 }; char *p; - if( !*homePath && com_homepath != NULL ) + if( !*homePath ) { -#ifdef __APPLE__ if( ( p = getenv( "HOME" ) ) != NULL ) { - Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); + Com_sprintf( homePath, sizeof(homePath), "%s%c%s", + p, PATH_SEP, "Library/Application Support/" ); - Q_strcat(homePath, sizeof(homePath), - "Library/Application Support/"); - - if(com_homepath->string[0]) + if( com_homepath && com_homepath->string[0] ) Q_strcat(homePath, sizeof(homePath), com_homepath->string); else - Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_MACOSX); + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME); } -#else - if( ( p = getenv( "FLATPAK_ID" ) ) != NULL && *p != '\0' ) - { - if( ( p = getenv( "XDG_DATA_HOME" ) ) != NULL && *p != '\0' ) - { - Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); - } - else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' ) - { - Com_sprintf(homePath, sizeof(homePath), "%s%c.local%cshare%c", p, PATH_SEP, PATH_SEP, PATH_SEP); - } - - if( *homePath ) - { - char *dir; - - if(com_homepath->string[0]) - dir = com_homepath->string; - else - dir = HOMEPATH_NAME_UNIX; - - if(dir[0] == '.' && dir[1] != '\0') - dir++; - - Q_strcat(homePath, sizeof(homePath), dir); - } - } - else if( ( p = getenv( "HOME" ) ) != NULL ) - { - Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); - - if(com_homepath->string[0]) - Q_strcat(homePath, sizeof(homePath), com_homepath->string); - else - Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_UNIX); - } -#endif } return homePath; } +char *Sys_DefaultHomeConfigPath(void) { return Sys_DefaultHomePath(); } +char *Sys_DefaultHomeDataPath(void) { return Sys_DefaultHomePath(); } +char *Sys_DefaultHomeStatePath(void) { return Sys_DefaultHomePath(); } + +#else // __APPLE__ + +/* +================== +Sys_HomeConfigPath +================== +*/ +char *Sys_HomeConfigPath(void) +{ + static char homeConfigPath[ MAX_OSPATH ] = { 0 }; + char *p; + + if( !*homeConfigPath ) + { + if( ( p = getenv( "XDG_CONFIG_HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeConfigPath, sizeof(homeConfigPath), "%s%c", p, PATH_SEP); + else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeConfigPath, sizeof(homeConfigPath), "%s%c.config%c", p, PATH_SEP, PATH_SEP); + + if( *homeConfigPath ) + { + if( com_homepath && com_homepath->string[0] ) + Q_strcat(homeConfigPath, sizeof(homeConfigPath), com_homepath->string); + else + Q_strcat(homeConfigPath, sizeof(homeConfigPath), HOMEPATH_NAME); + } + } + + return homeConfigPath; +} + +/* +================== +Sys_HomeDataPath +================== +*/ +char *Sys_HomeDataPath(void) +{ + static char homeDataPath[ MAX_OSPATH ] = { 0 }; + char *p; + + if( !*homeDataPath ) + { + if( ( p = getenv( "XDG_DATA_HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeDataPath, sizeof(homeDataPath), "%s%c", p, PATH_SEP); + else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeDataPath, sizeof(homeDataPath), "%s%c.local%cshare%c", p, PATH_SEP, PATH_SEP, PATH_SEP); + + if( *homeDataPath ) + { + if( com_homepath && com_homepath->string[0] ) + Q_strcat(homeDataPath, sizeof(homeDataPath), com_homepath->string); + else + Q_strcat(homeDataPath, sizeof(homeDataPath), HOMEPATH_NAME); + } + } + + return homeDataPath; +} + +/* +================== +Sys_HomeStatePath +================== +*/ +char *Sys_HomeStatePath(void) +{ + static char homeStatePath[ MAX_OSPATH ] = { 0 }; + char *p; + + if( !*homeStatePath ) + { + if( ( p = getenv( "XDG_STATE_HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeStatePath, sizeof(homeStatePath), "%s%c", p, PATH_SEP); + else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' ) + Com_sprintf(homeStatePath, sizeof(homeStatePath), "%s%c.local%cstate%c", p, PATH_SEP, PATH_SEP, PATH_SEP); + + if( *homeStatePath ) + { + if( com_homepath && com_homepath->string[0] ) + Q_strcat(homeStatePath, sizeof(homeStatePath), com_homepath->string); + else + Q_strcat(homeStatePath, sizeof(homeStatePath), HOMEPATH_NAME); + } + } + + return homeStatePath; +} + +/* +================== +Sys_LegacyHomePath +================== +*/ +static char *Sys_LegacyHomePath(void) +{ + static char homePath[ MAX_OSPATH ] = { 0 }; + char *p; + + if( ( p = getenv( "FLATPAK_ID" ) ) != NULL && *p != '\0' ) + { + // Flatpaks always use XDG + return ""; + } + + if( !*homePath ) + { + if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' ) + { + Com_sprintf(homePath, sizeof(homePath), "%s%c%s", + p, PATH_SEP, HOMEPATH_NAME_UNIX_LEGACY); + } + } + + return homePath; +} + +/* +================== +Sys_MigrateToXDG +================== +*/ +qboolean Sys_MigrateToXDG(void) +{ + const char *scriptTemplate = + "#!/bin/sh\n" + + "set -eu\n" + + "legacy_home=\"%s\"\n" + "xdg_config_home=\"%s\"\n" + "xdg_data_home=\"%s\"\n" + "xdg_state_home=\"%s\"\n" + + "xdg_config_pattern=\"*.cfg\"\n" + "xdg_data_pattern=\"demos/*.dm_* *.log *.pk3 *.txt \\\n" + " screenshots/*.jpg screenshots/*.tga videos/*.avi\"\n" + "xdg_state_pattern=\"*.dat q3history q3key\"\n" + + "glob_copy() {\n" + " game_dir=${1:+$1/}\n" + " dst=\"$2\"\n" + " shift 2\n" + " for pattern in \"$@\"; do\n" + " subdir=$(dirname \"$pattern\")\n" + " [ \"$subdir\" = \".\" ] && subdir=\"\"\n" + " find \"$legacy_home/$game_dir\" \\\n" + " -path \"$legacy_home/$game_dir$pattern\" -type f \\\n" + " -exec mkdir -p \"$dst/$game_dir$subdir\" \\; \\\n" + " -exec cp -av {} \"$dst/$game_dir$subdir\" \\;\n" + " done\n" + "}\n" + + "unmatched_copy() {\n" + " game_dir=${1:+$1/}\n" + " shift\n" + " find_args=\"\"\n" + " for pattern in \"$@\"; do\n" + " find_args=\"$find_args \\\n" + " -not -path \\\"$legacy_home/$game_dir$pattern\\\"\"\n" + " done\n" + " eval \"find '$legacy_home/$game_dir' -type f $find_args\" | \\\n" + " while IFS= read -r file; do\n" + " dst=\"$xdg_data_home${file#$legacy_home}\"\n" + " dst_dir=$(dirname \"$dst\")\n" + " mkdir -p \"$dst_dir\"\n" + " cp -av \"$file\" \"$dst\"\n" + " done\n" + "}\n" + + "echo \"Starting XDG migration...\"\n" + + "glob_copy \"\" \"$xdg_state_home\" \"qkey\"\n" + + "for game_dir in \"$legacy_home\"/*; do\n" + " [ -d \"$game_dir\" ] || continue\n" + " game=$(basename \"$game_dir\")\n" + " glob_copy \"$game\" \"$xdg_config_home\" $xdg_config_pattern\n" + " glob_copy \"$game\" \"$xdg_data_home\" $xdg_data_pattern\n" + " glob_copy \"$game\" \"$xdg_state_home\" $xdg_state_pattern\n" + " unmatched_copy \"$game\" \\\n" + " $xdg_config_pattern \\\n" + " $xdg_data_pattern \\\n" + " $xdg_state_pattern\n" + "done\n" + + "echo \"XDG migration complete!\"\n"; + + char scriptBuffer[2048]; + int len = Com_sprintf( scriptBuffer, sizeof( scriptBuffer ), scriptTemplate, + Sys_LegacyHomePath( ), Sys_HomeConfigPath( ), + Sys_HomeDataPath( ), Sys_HomeStatePath( ) ); + + if( len < 0 || len >= (int)sizeof( scriptBuffer ) ) + { + Com_Printf( "XDG migration error: substitution failed.\n" ); + return qfalse; + } + + char scriptPath[] = "/tmp/xdgmigrationXXXXXX"; + int fd = mkstemp( scriptPath ); + if( fd == -1 ) + { + Com_Printf( "XDG migration error: script creation failed.\n" ); + return qfalse; + } + + if( write( fd, scriptBuffer, len ) != len ) + { + close( fd ); + unlink( scriptPath ); + Com_Printf( "XDG migration error: script write failed.\n" ); + return qfalse; + } + close( fd ); + + if( chmod( scriptPath, 0700 ) == -1 ) + { + unlink( scriptPath ); + Com_Printf( "XDG migration error: script chmod failed.\n" ); + return qfalse; + } + + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( scriptPath ); + int result = Sys_Exec( ); + unlink( scriptPath ); + + return result == 0; +} + +/* +================== +Sys_ShouldUseLegacyHomePath +================== +*/ +static qboolean Sys_ShouldUseLegacyHomePath(void) +{ + if( access( Sys_HomeConfigPath( ), F_OK ) == 0 ) + { + // If the XDG config directory exists, prefer XDG layout, regardless + return qfalse; + } + + if( ( com_homepath && com_homepath->string[0] ) || + Cvar_VariableString( "fs_homepath" )[0] ) + { + // If a custom homepath has been explicity set then + // that strongly implies that migration isn't desired + return qfalse; + } + + const char *legacyHomePath = Sys_LegacyHomePath(); + + if( !*legacyHomePath || access( legacyHomePath, F_OK ) != 0 ) + { + // The legacy home path doesn't exist + return qfalse; + } + + char migrationRefusedPath[ MAX_OSPATH ]; + Com_sprintf( migrationRefusedPath, sizeof( migrationRefusedPath ), + "%s/.xdgMigrationRefused", legacyHomePath ); + + // If the user hasn't already refused, ask if they want to migrate + if( access( migrationRefusedPath, F_OK ) != 0 ) + { + dialogResult_t result = Sys_Dialog( DT_YES_NO, va( + "Modern games and applications store files in " + "directories according to the Free Desktop standard. " + "Here's what that would look like for %s:\n\n" + "Configuration files:\n %s\n\n" + "Data files; pk3s, screenshots, logs, demos, etc.:\n %s\n\n" + "Internal runtime files:\n %s\n\n" + "At the moment all of these files are found here:\n %s\n\n" + "Do you want to copy your files to these new directories?", + PRODUCT_NAME, + Sys_HomeConfigPath( ), Sys_HomeDataPath( ), Sys_HomeStatePath( ), + legacyHomePath ), + "Home Directory Files Upgrade" ); + + if( result == DR_YES ) + return !Sys_MigrateToXDG( ); + + // Guard against asking again in future + fclose( fopen( migrationRefusedPath, "w" ) ); + } + + return qtrue; +} + +/* +================== +Sys_DefaultHomeConfigPath +================== +*/ +char *Sys_DefaultHomeConfigPath(void) +{ + if( Sys_ShouldUseLegacyHomePath( ) ) + return Sys_LegacyHomePath( ); + + return Sys_HomeConfigPath( ); +} + +/* +================== +Sys_DefaultHomeDataPath +================== +*/ +char *Sys_DefaultHomeDataPath(void) +{ + if( Sys_ShouldUseLegacyHomePath( ) ) + return Sys_LegacyHomePath( ); + + return Sys_HomeDataPath( ); +} + +/* +================== +Sys_DefaultHomeStatePath +================== +*/ +char *Sys_DefaultHomeStatePath(void) +{ + if( Sys_ShouldUseLegacyHomePath( ) ) + return Sys_LegacyHomePath( ); + + return Sys_HomeStatePath( ); +} + +#endif + /* ================ Sys_SteamPath @@ -668,10 +966,10 @@ void Sys_ErrorDialog( const char *error ) char buffer[ 1024 ]; unsigned int size; int f = -1; - const char *homepath = Cvar_VariableString( "fs_homepath" ); + const char *homedatapath = Cvar_VariableString( "fs_homedatapath" ); const char *gamedir = Cvar_VariableString( "fs_game" ); const char *fileName = "crashlog.txt"; - char *ospath = FS_BuildOSPath( homepath, gamedir, fileName ); + char *ospath = FS_BuildOSPath( homedatapath, gamedir, fileName ); Sys_Print( va( "%s\n", error ) ); @@ -680,7 +978,7 @@ void Sys_ErrorDialog( const char *error ) #endif // Make sure the write path for the crashlog exists... - if( FS_CreatePath( ospath ) ) + if( FS_CreatePath( homedatapath ) ) { Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", ospath); return; diff --git a/code/sys/sys_win32.c b/code/sys/sys_win32.c index 7712e8b3..20ab29a2 100644 --- a/code/sys/sys_win32.c +++ b/code/sys/sys_win32.c @@ -88,7 +88,7 @@ void Sys_SetFloatEnv(void) Sys_DefaultHomePath ================ */ -char *Sys_DefaultHomePath( void ) +static char *Sys_DefaultHomePath( void ) { static char homePath[ MAX_OSPATH ] = { 0 }; @@ -108,12 +108,16 @@ char *Sys_DefaultHomePath( void ) if(com_homepath->string[0]) Q_strcat(homePath, sizeof(homePath), com_homepath->string); else - Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_WIN); + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME); } return homePath; } +char *Sys_DefaultHomeConfigPath(void) { return Sys_DefaultHomePath(); } +char *Sys_DefaultHomeDataPath(void) { return Sys_DefaultHomePath(); } +char *Sys_DefaultHomeStatePath(void) { return Sys_DefaultHomePath(); } + /* ================ Sys_SteamPath