vrpathregistry_public.cpp (12070B)
1 //========= Copyright Valve Corporation ============// 2 3 #include "vrpathregistry_public.h" 4 #include "json/json.h" 5 #include "pathtools_public.h" 6 #include "envvartools_public.h" 7 #include "strtools_public.h" 8 #include "dirtools_public.h" 9 10 #if defined( WIN32 ) 11 #include <windows.h> 12 #include <shlobj.h> 13 14 #undef GetEnvironmentVariable 15 #elif defined OSX 16 #include <Foundation/Foundation.h> 17 #include <AppKit/AppKit.h> 18 #elif defined(LINUX) 19 #include <dlfcn.h> 20 #include <stdio.h> 21 #endif 22 23 #include <algorithm> 24 25 #ifndef VRLog 26 #if defined( __MINGW32__ ) 27 #define VRLog(args...) fprintf(stderr, args) 28 #elif defined( WIN32 ) 29 #define VRLog(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) 30 #else 31 #define VRLog(args...) fprintf(stderr, args) 32 #endif 33 #endif 34 35 /** Returns the root of the directory the system wants us to store user config data in */ 36 static std::string GetAppSettingsPath() 37 { 38 #if defined( WIN32 ) 39 WCHAR rwchPath[MAX_PATH]; 40 41 if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) ) 42 { 43 return ""; 44 } 45 46 // Convert the path to UTF-8 and store in the output 47 std::string sUserPath = UTF16to8( rwchPath ); 48 49 return sUserPath; 50 #elif defined( OSX ) 51 std::string sSettingsDir; 52 @autoreleasepool { 53 // Search for the path 54 NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES ); 55 if ( [paths count] == 0 ) 56 { 57 return ""; 58 } 59 60 NSString *resolvedPath = [paths objectAtIndex:0]; 61 resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"]; 62 63 if ( ![[NSFileManager defaultManager] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] ) 64 { 65 return ""; 66 } 67 68 sSettingsDir.assign( [resolvedPath UTF8String] ); 69 } 70 return sSettingsDir; 71 #elif defined( LINUX ) 72 73 // As defined by XDG Base Directory Specification 74 // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 75 76 const char *pchHome = getenv("XDG_CONFIG_HOME"); 77 if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) ) 78 { 79 return pchHome; 80 } 81 82 // 83 // XDG_CONFIG_HOME is not defined, use ~/.config instead 84 // 85 pchHome = getenv( "HOME" ); 86 if ( pchHome == NULL ) 87 { 88 return ""; 89 } 90 91 std::string sUserPath( pchHome ); 92 sUserPath = Path_Join( sUserPath, ".config" ); 93 return sUserPath; 94 #else 95 #warning "Unsupported platform" 96 #endif 97 } 98 99 100 // --------------------------------------------------------------------------- 101 // Purpose: Constructor 102 // --------------------------------------------------------------------------- 103 CVRPathRegistry_Public::CVRPathRegistry_Public() 104 { 105 106 } 107 108 // --------------------------------------------------------------------------- 109 // Purpose: Computes the registry filename 110 // --------------------------------------------------------------------------- 111 std::string CVRPathRegistry_Public::GetOpenVRConfigPath() 112 { 113 std::string sConfigPath = GetAppSettingsPath(); 114 if( sConfigPath.empty() ) 115 return ""; 116 117 #if defined( _WIN32 ) || defined( LINUX ) 118 sConfigPath = Path_Join( sConfigPath, "openvr" ); 119 #elif defined ( OSX ) 120 sConfigPath = Path_Join( sConfigPath, ".openvr" ); 121 #else 122 #warning "Unsupported platform" 123 #endif 124 sConfigPath = Path_FixSlashes( sConfigPath ); 125 return sConfigPath; 126 } 127 128 129 130 //----------------------------------------------------------------------------- 131 // Purpose: 132 //----------------------------------------------------------------------------- 133 std::string CVRPathRegistry_Public::GetVRPathRegistryFilename() 134 { 135 std::string sOverridePath = GetEnvironmentVariable( "VR_PATHREG_OVERRIDE" ); 136 if ( !sOverridePath.empty() ) 137 return sOverridePath; 138 139 std::string sPath = GetOpenVRConfigPath(); 140 if ( sPath.empty() ) 141 return ""; 142 143 #if defined( _WIN32 ) 144 sPath = Path_Join( sPath, "openvrpaths.vrpath" ); 145 #elif defined ( POSIX ) 146 sPath = Path_Join( sPath, "openvrpaths.vrpath" ); 147 #else 148 #error "Unsupported platform" 149 #endif 150 sPath = Path_FixSlashes( sPath ); 151 return sPath; 152 } 153 154 155 // --------------------------------------------------------------------------- 156 // Purpose: Converts JSON to a history array 157 // --------------------------------------------------------------------------- 158 static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName ) 159 { 160 if( !root.isMember( pchArrayName ) ) 161 return; 162 163 const Json::Value & arrayNode = root[ pchArrayName ]; 164 if( !arrayNode ) 165 { 166 VRLog( "VR Path Registry node %s is not an array\n", pchArrayName ); 167 return; 168 } 169 170 pvecHistory->clear(); 171 pvecHistory->reserve( arrayNode.size() ); 172 for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ ) 173 { 174 std::string sPath( arrayNode[ unIndex ].asString() ); 175 pvecHistory->push_back( sPath ); 176 } 177 } 178 179 180 // --------------------------------------------------------------------------- 181 // Purpose: Converts a history array to JSON 182 // --------------------------------------------------------------------------- 183 static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName ) 184 { 185 Json::Value & arrayNode = root[ pchArrayName ]; 186 for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ ) 187 { 188 arrayNode.append( *i ); 189 } 190 } 191 192 193 //----------------------------------------------------------------------------- 194 // Purpose: 195 //----------------------------------------------------------------------------- 196 bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString ) 197 { 198 std::string sRegPath = GetVRPathRegistryFilename(); 199 if( sRegPath.empty() ) 200 return false; 201 202 std::string sRegistryContents = Path_ReadTextFile( sRegPath ); 203 if( sRegistryContents.empty() ) 204 return false; 205 206 sJsonString = sRegistryContents; 207 208 return true; 209 } 210 211 // Mozilla: see mozilla.patch for more details 212 // --------------------------------------------------------------------------- 213 // Purpose: Loads the config file from its well known location 214 // --------------------------------------------------------------------------- 215 bool CVRPathRegistry_Public::BLoadFromFile( std::string *psLoadError ) 216 { 217 std::string sRegPath = GetVRPathRegistryFilename(); 218 if( sRegPath.empty() ) 219 { 220 if ( psLoadError ) 221 { 222 *psLoadError = "Unable to determine VR Path Registry filename"; 223 } 224 return false; 225 } 226 227 std::string sRegistryContents = Path_ReadTextFile( sRegPath ); 228 if( sRegistryContents.empty() ) 229 { 230 if ( psLoadError ) 231 { 232 *psLoadError = "Unable to read VR Path Registry from " + sRegPath; 233 } 234 return false; 235 } 236 237 Json::Value root; 238 Json::CharReaderBuilder builder; 239 std::istringstream istream( sRegistryContents ); 240 std::string sErrors; 241 242 // try 243 { 244 if ( !parseFromStream( builder, istream, &root, &sErrors ) ) 245 { 246 if ( psLoadError ) 247 { 248 *psLoadError = "Unable to parse " + sRegPath + ": " + sErrors; 249 } 250 return false; 251 } 252 253 ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" ); 254 ParseStringListFromJson( &m_vecConfigPath, root, "config" ); 255 ParseStringListFromJson( &m_vecLogPath, root, "log" ); 256 if ( root.isMember( "external_drivers" ) && root["external_drivers"].isArray() ) 257 { 258 ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" ); 259 } 260 } 261 // catch ( ... ) 262 // { 263 // if ( psLoadError ) 264 // { 265 // *psLoadError = "Unable to parse " + sRegPath + ": exception thrown in JSON library"; 266 // } 267 // return false; 268 // } 269 270 return true; 271 } 272 273 274 // --------------------------------------------------------------------------- 275 // Purpose: Saves the config file to its well known location 276 // --------------------------------------------------------------------------- 277 bool CVRPathRegistry_Public::BSaveToFile() const 278 { 279 std::string sRegPath = GetVRPathRegistryFilename(); 280 if( sRegPath.empty() ) 281 return false; 282 283 Json::Value root; 284 285 root[ "version" ] = 1; 286 root[ "jsonid" ] = "vrpathreg"; 287 288 StringListToJson( m_vecRuntimePath, root, "runtime" ); 289 StringListToJson( m_vecConfigPath, root, "config" ); 290 StringListToJson( m_vecLogPath, root, "log" ); 291 StringListToJson( m_vecExternalDrivers, root, "external_drivers" ); 292 293 Json::StreamWriterBuilder builder; 294 std::string sRegistryContents = Json::writeString( builder, root ); 295 296 // make sure the directory we're writing into actually exists 297 std::string sRegDirectory = Path_StripFilename( sRegPath ); 298 if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) ) 299 { 300 VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() ); 301 return false; 302 } 303 304 if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) ) 305 { 306 VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() ); 307 return false; 308 } 309 310 return true; 311 } 312 313 314 // --------------------------------------------------------------------------- 315 // Purpose: Returns the current runtime path or NULL if no path is configured. 316 // --------------------------------------------------------------------------- 317 std::string CVRPathRegistry_Public::GetRuntimePath() const 318 { 319 if( m_vecRuntimePath.empty() ) 320 return ""; 321 else 322 return m_vecRuntimePath.front().c_str(); 323 } 324 325 326 // --------------------------------------------------------------------------- 327 // Purpose: Returns the current config path or NULL if no path is configured. 328 // --------------------------------------------------------------------------- 329 std::string CVRPathRegistry_Public::GetConfigPath() const 330 { 331 if( m_vecConfigPath.empty() ) 332 return ""; 333 else 334 return m_vecConfigPath.front().c_str(); 335 } 336 337 338 // --------------------------------------------------------------------------- 339 // Purpose: Returns the current log path or NULL if no path is configured. 340 // --------------------------------------------------------------------------- 341 std::string CVRPathRegistry_Public::GetLogPath() const 342 { 343 if( m_vecLogPath.empty() ) 344 return ""; 345 else 346 return m_vecLogPath.front().c_str(); 347 } 348 349 350 351 // --------------------------------------------------------------------------- 352 // Purpose: Returns paths using the path registry and the provided override 353 // values. Pass NULL for any paths you don't care about. 354 // --------------------------------------------------------------------------- 355 bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers ) 356 { 357 std::string sLoadError; 358 CVRPathRegistry_Public pathReg; 359 bool bLoadedRegistry = pathReg.BLoadFromFile( &sLoadError ); 360 int nCountEnvironmentVariables = 0; 361 int nRequestedPaths = 0; 362 363 if( psRuntimePath ) 364 { 365 nRequestedPaths++; 366 if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 ) 367 { 368 *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar ); 369 nCountEnvironmentVariables++; 370 } 371 else if( !pathReg.GetRuntimePath().empty() ) 372 { 373 *psRuntimePath = pathReg.GetRuntimePath(); 374 } 375 else 376 { 377 *psRuntimePath = ""; 378 } 379 } 380 381 if( psConfigPath ) 382 { 383 nRequestedPaths++; 384 if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 ) 385 { 386 *psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar ); 387 nCountEnvironmentVariables++; 388 } 389 else if( pchConfigPathOverride ) 390 { 391 *psConfigPath = pchConfigPathOverride; 392 } 393 else if( !pathReg.GetConfigPath().empty() ) 394 { 395 *psConfigPath = pathReg.GetConfigPath(); 396 } 397 else 398 { 399 *psConfigPath = ""; 400 } 401 } 402 403 if( psLogPath ) 404 { 405 nRequestedPaths++; 406 if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 ) 407 { 408 *psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar ); 409 nCountEnvironmentVariables++; 410 } 411 else if( pchLogPathOverride ) 412 { 413 *psLogPath = pchLogPathOverride; 414 } 415 else if( !pathReg.GetLogPath().empty() ) 416 { 417 *psLogPath = pathReg.GetLogPath(); 418 } 419 else 420 { 421 *psLogPath = ""; 422 } 423 } 424 425 if ( pvecExternalDrivers ) 426 { 427 *pvecExternalDrivers = pathReg.m_vecExternalDrivers; 428 } 429 430 if ( nCountEnvironmentVariables == nRequestedPaths ) 431 { 432 // all three environment variables were set, so we don't need the physical file 433 return true; 434 } 435 else if( !bLoadedRegistry ) 436 { 437 VRLog( "%s\n", sLoadError.c_str() ); 438 } 439 440 return bLoadedRegistry; 441 }