| //========= Copyright Valve Corporation ============// |
| |
| #include "vrpathregistry_public.h" |
| #include "json/json.h" |
| #include "pathtools_public.h" |
| #include "envvartools_public.h" |
| #include "strtools_public.h" |
| #include "dirtools_public.h" |
| |
| #if defined( WIN32 ) |
| #include <windows.h> |
| #include <shlobj.h> |
| |
| #undef GetEnvironmentVariable |
| #elif defined OSX |
| #include <Foundation/Foundation.h> |
| #include <AppKit/AppKit.h> |
| #elif defined(LINUX) |
| #include <dlfcn.h> |
| #include <stdio.h> |
| #endif |
| |
| #include <algorithm> |
| |
| #ifndef VRLog |
| #if defined( __MINGW32__ ) |
| #define VRLog(args...) fprintf(stderr, args) |
| #elif defined( WIN32 ) |
| #define VRLog(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) |
| #else |
| #define VRLog(args...) fprintf(stderr, args) |
| #endif |
| #endif |
| |
| /** Returns the root of the directory the system wants us to store user config data in */ |
| static std::string GetAppSettingsPath() |
| { |
| #if defined( WIN32 ) |
| WCHAR rwchPath[MAX_PATH]; |
| |
| if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) ) |
| { |
| return ""; |
| } |
| |
| // Convert the path to UTF-8 and store in the output |
| std::string sUserPath = UTF16to8( rwchPath ); |
| |
| return sUserPath; |
| #elif defined( OSX ) |
| std::string sSettingsDir; |
| @autoreleasepool { |
| // Search for the path |
| NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES ); |
| if ( [paths count] == 0 ) |
| { |
| return ""; |
| } |
| |
| NSString *resolvedPath = [paths objectAtIndex:0]; |
| resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"]; |
| |
| if ( ![[NSFileManager new] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] ) |
| { |
| return ""; |
| } |
| |
| sSettingsDir.assign( [resolvedPath UTF8String] ); |
| } |
| return sSettingsDir; |
| #elif defined( LINUX ) |
| |
| // As defined by XDG Base Directory Specification |
| // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html |
| |
| const char *pchHome = getenv("XDG_CONFIG_HOME"); |
| if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) ) |
| { |
| return pchHome; |
| } |
| |
| // |
| // XDG_CONFIG_HOME is not defined, use ~/.config instead |
| // |
| pchHome = getenv( "HOME" ); |
| if ( pchHome == NULL ) |
| { |
| return ""; |
| } |
| |
| std::string sUserPath( pchHome ); |
| sUserPath = Path_Join( sUserPath, ".config" ); |
| return sUserPath; |
| #else |
| #warning "Unsupported platform" |
| #endif |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Constructor |
| // --------------------------------------------------------------------------- |
| CVRPathRegistry_Public::CVRPathRegistry_Public() |
| { |
| |
| } |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Computes the registry filename |
| // --------------------------------------------------------------------------- |
| std::string CVRPathRegistry_Public::GetOpenVRConfigPath() |
| { |
| std::string sConfigPath = GetAppSettingsPath(); |
| if( sConfigPath.empty() ) |
| return ""; |
| |
| #if defined( _WIN32 ) || defined( LINUX ) |
| sConfigPath = Path_Join( sConfigPath, "openvr" ); |
| #elif defined ( OSX ) |
| sConfigPath = Path_Join( sConfigPath, ".openvr" ); |
| #else |
| #warning "Unsupported platform" |
| #endif |
| sConfigPath = Path_FixSlashes( sConfigPath ); |
| return sConfigPath; |
| } |
| |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| std::string CVRPathRegistry_Public::GetVRPathRegistryFilename() |
| { |
| std::string sPath = GetOpenVRConfigPath(); |
| if ( sPath.empty() ) |
| return ""; |
| |
| #if defined( _WIN32 ) |
| sPath = Path_Join( sPath, "openvrpaths.vrpath" ); |
| #elif defined ( POSIX ) |
| sPath = Path_Join( sPath, "openvrpaths.vrpath" ); |
| #else |
| #error "Unsupported platform" |
| #endif |
| sPath = Path_FixSlashes( sPath ); |
| return sPath; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Converts JSON to a history array |
| // --------------------------------------------------------------------------- |
| static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName ) |
| { |
| if( !root.isMember( pchArrayName ) ) |
| return; |
| |
| const Json::Value & arrayNode = root[ pchArrayName ]; |
| if( !arrayNode ) |
| { |
| VRLog( "VR Path Registry node %s is not an array\n", pchArrayName ); |
| return; |
| } |
| |
| pvecHistory->clear(); |
| pvecHistory->reserve( arrayNode.size() ); |
| for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ ) |
| { |
| std::string sPath( arrayNode[ unIndex ].asString() ); |
| pvecHistory->push_back( sPath ); |
| } |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Converts a history array to JSON |
| // --------------------------------------------------------------------------- |
| static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName ) |
| { |
| Json::Value & arrayNode = root[ pchArrayName ]; |
| for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ ) |
| { |
| arrayNode.append( *i ); |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString ) |
| { |
| std::string sRegPath = GetVRPathRegistryFilename(); |
| if( sRegPath.empty() ) |
| return false; |
| |
| std::string sRegistryContents = Path_ReadTextFile( sRegPath ); |
| if( sRegistryContents.empty() ) |
| return false; |
| |
| sJsonString = sRegistryContents; |
| |
| return true; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Loads the config file from its well known location |
| // --------------------------------------------------------------------------- |
| bool CVRPathRegistry_Public::BLoadFromFile() |
| { |
| std::string sRegPath = GetVRPathRegistryFilename(); |
| if( sRegPath.empty() ) |
| { |
| VRLog( "Unable to determine VR Path Registry filename\n" ); |
| return false; |
| } |
| |
| std::string sRegistryContents = Path_ReadTextFile( sRegPath ); |
| if( sRegistryContents.empty() ) |
| { |
| VRLog( "Unable to read VR Path Registry from %s\n", sRegPath.c_str() ); |
| return false; |
| } |
| |
| Json::Value root; |
| Json::Reader reader; |
| |
| if( !reader.parse( sRegistryContents, root ) ) |
| { |
| VRLog( "Unable to parse %s: %s\n", sRegPath.c_str(), reader.getFormattedErrorMessages().c_str() ); |
| return false; |
| } |
| |
| ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" ); |
| ParseStringListFromJson( &m_vecConfigPath, root, "config" ); |
| ParseStringListFromJson( &m_vecLogPath, root, "log" ); |
| if (root.isMember( "external_drivers" ) && root[ "external_drivers" ].isArray() ) |
| { |
| ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" ); |
| } |
| |
| return true; |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Saves the config file to its well known location |
| // --------------------------------------------------------------------------- |
| bool CVRPathRegistry_Public::BSaveToFile() const |
| { |
| #if defined( DASHBOARD_BUILD_MODE ) |
| return false; |
| #else |
| std::string sRegPath = GetVRPathRegistryFilename(); |
| if( sRegPath.empty() ) |
| return false; |
| |
| Json::Value root; |
| |
| root[ "version" ] = 1; |
| root[ "jsonid" ] = "vrpathreg"; |
| |
| StringListToJson( m_vecRuntimePath, root, "runtime" ); |
| StringListToJson( m_vecConfigPath, root, "config" ); |
| StringListToJson( m_vecLogPath, root, "log" ); |
| StringListToJson( m_vecExternalDrivers, root, "external_drivers" ); |
| |
| Json::StyledWriter writer; |
| std::string sRegistryContents = writer.write( root ); |
| |
| // make sure the directory we're writing into actually exists |
| std::string sRegDirectory = Path_StripFilename( sRegPath ); |
| if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) ) |
| { |
| VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() ); |
| return false; |
| } |
| |
| if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) ) |
| { |
| VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() ); |
| return false; |
| } |
| |
| return true; |
| #endif |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Returns the current runtime path or NULL if no path is configured. |
| // --------------------------------------------------------------------------- |
| std::string CVRPathRegistry_Public::GetRuntimePath() const |
| { |
| if( m_vecRuntimePath.empty() ) |
| return ""; |
| else |
| return m_vecRuntimePath.front().c_str(); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Returns the current config path or NULL if no path is configured. |
| // --------------------------------------------------------------------------- |
| std::string CVRPathRegistry_Public::GetConfigPath() const |
| { |
| if( m_vecConfigPath.empty() ) |
| return ""; |
| else |
| return m_vecConfigPath.front().c_str(); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Returns the current log path or NULL if no path is configured. |
| // --------------------------------------------------------------------------- |
| std::string CVRPathRegistry_Public::GetLogPath() const |
| { |
| if( m_vecLogPath.empty() ) |
| return ""; |
| else |
| return m_vecLogPath.front().c_str(); |
| } |
| |
| |
| |
| // --------------------------------------------------------------------------- |
| // Purpose: Returns paths using the path registry and the provided override |
| // values. Pass NULL for any paths you don't care about. |
| // --------------------------------------------------------------------------- |
| bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers ) |
| { |
| CVRPathRegistry_Public pathReg; |
| bool bLoadedRegistry = pathReg.BLoadFromFile(); |
| int nCountEnvironmentVariables = 0; |
| |
| if( psRuntimePath ) |
| { |
| if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 ) |
| { |
| *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar ); |
| nCountEnvironmentVariables++; |
| } |
| else if( !pathReg.GetRuntimePath().empty() ) |
| { |
| *psRuntimePath = pathReg.GetRuntimePath(); |
| } |
| else |
| { |
| *psRuntimePath = ""; |
| } |
| } |
| |
| if( psConfigPath ) |
| { |
| if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 ) |
| { |
| *psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar ); |
| nCountEnvironmentVariables++; |
| } |
| else if( pchConfigPathOverride ) |
| { |
| *psConfigPath = pchConfigPathOverride; |
| } |
| else if( !pathReg.GetConfigPath().empty() ) |
| { |
| *psConfigPath = pathReg.GetConfigPath(); |
| } |
| else |
| { |
| *psConfigPath = ""; |
| } |
| } |
| |
| if( psLogPath ) |
| { |
| if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 ) |
| { |
| *psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar ); |
| nCountEnvironmentVariables++; |
| } |
| else if( pchLogPathOverride ) |
| { |
| *psLogPath = pchLogPathOverride; |
| } |
| else if( !pathReg.GetLogPath().empty() ) |
| { |
| *psLogPath = pathReg.GetLogPath(); |
| } |
| else |
| { |
| *psLogPath = ""; |
| } |
| } |
| |
| if ( pvecExternalDrivers ) |
| { |
| *pvecExternalDrivers = pathReg.m_vecExternalDrivers; |
| } |
| |
| if ( nCountEnvironmentVariables == 3 ) |
| { |
| // all three environment variables where set, so we don't need the physical file |
| return true; |
| } |
| |
| return bLoadedRegistry; |
| } |
| |