Line data Source code
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 0 : 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 new] 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 0 : const char *pchHome = getenv("XDG_CONFIG_HOME");
77 0 : if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) )
78 : {
79 0 : return pchHome;
80 : }
81 :
82 : //
83 : // XDG_CONFIG_HOME is not defined, use ~/.config instead
84 : //
85 0 : pchHome = getenv( "HOME" );
86 0 : if ( pchHome == NULL )
87 : {
88 0 : return "";
89 : }
90 :
91 0 : std::string sUserPath( pchHome );
92 0 : sUserPath = Path_Join( sUserPath, ".config" );
93 0 : return sUserPath;
94 : #else
95 : #warning "Unsupported platform"
96 : #endif
97 : }
98 :
99 :
100 : // ---------------------------------------------------------------------------
101 : // Purpose: Constructor
102 : // ---------------------------------------------------------------------------
103 0 : CVRPathRegistry_Public::CVRPathRegistry_Public()
104 : {
105 :
106 0 : }
107 :
108 : // ---------------------------------------------------------------------------
109 : // Purpose: Computes the registry filename
110 : // ---------------------------------------------------------------------------
111 0 : std::string CVRPathRegistry_Public::GetOpenVRConfigPath()
112 : {
113 0 : std::string sConfigPath = GetAppSettingsPath();
114 0 : if( sConfigPath.empty() )
115 0 : return "";
116 :
117 : #if defined( _WIN32 ) || defined( LINUX )
118 0 : sConfigPath = Path_Join( sConfigPath, "openvr" );
119 : #elif defined ( OSX )
120 : sConfigPath = Path_Join( sConfigPath, ".openvr" );
121 : #else
122 : #warning "Unsupported platform"
123 : #endif
124 0 : sConfigPath = Path_FixSlashes( sConfigPath );
125 0 : return sConfigPath;
126 : }
127 :
128 :
129 :
130 : //-----------------------------------------------------------------------------
131 : // Purpose:
132 : //-----------------------------------------------------------------------------
133 0 : std::string CVRPathRegistry_Public::GetVRPathRegistryFilename()
134 : {
135 0 : std::string sPath = GetOpenVRConfigPath();
136 0 : if ( sPath.empty() )
137 0 : return "";
138 :
139 : #if defined( _WIN32 )
140 : sPath = Path_Join( sPath, "openvrpaths.vrpath" );
141 : #elif defined ( POSIX )
142 0 : sPath = Path_Join( sPath, "openvrpaths.vrpath" );
143 : #else
144 : #error "Unsupported platform"
145 : #endif
146 0 : sPath = Path_FixSlashes( sPath );
147 0 : return sPath;
148 : }
149 :
150 :
151 : // ---------------------------------------------------------------------------
152 : // Purpose: Converts JSON to a history array
153 : // ---------------------------------------------------------------------------
154 0 : static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName )
155 : {
156 0 : if( !root.isMember( pchArrayName ) )
157 0 : return;
158 :
159 0 : const Json::Value & arrayNode = root[ pchArrayName ];
160 0 : if( !arrayNode )
161 : {
162 0 : VRLog( "VR Path Registry node %s is not an array\n", pchArrayName );
163 0 : return;
164 : }
165 :
166 0 : pvecHistory->clear();
167 0 : pvecHistory->reserve( arrayNode.size() );
168 0 : for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ )
169 : {
170 0 : std::string sPath( arrayNode[ unIndex ].asString() );
171 0 : pvecHistory->push_back( sPath );
172 : }
173 : }
174 :
175 :
176 : // ---------------------------------------------------------------------------
177 : // Purpose: Converts a history array to JSON
178 : // ---------------------------------------------------------------------------
179 0 : static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName )
180 : {
181 0 : Json::Value & arrayNode = root[ pchArrayName ];
182 0 : for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ )
183 : {
184 0 : arrayNode.append( *i );
185 : }
186 0 : }
187 :
188 :
189 : //-----------------------------------------------------------------------------
190 : // Purpose:
191 : //-----------------------------------------------------------------------------
192 0 : bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString )
193 : {
194 0 : std::string sRegPath = GetVRPathRegistryFilename();
195 0 : if( sRegPath.empty() )
196 0 : return false;
197 :
198 0 : std::string sRegistryContents = Path_ReadTextFile( sRegPath );
199 0 : if( sRegistryContents.empty() )
200 0 : return false;
201 :
202 0 : sJsonString = sRegistryContents;
203 :
204 0 : return true;
205 : }
206 :
207 :
208 : // ---------------------------------------------------------------------------
209 : // Purpose: Loads the config file from its well known location
210 : // ---------------------------------------------------------------------------
211 0 : bool CVRPathRegistry_Public::BLoadFromFile()
212 : {
213 0 : std::string sRegPath = GetVRPathRegistryFilename();
214 0 : if( sRegPath.empty() )
215 : {
216 0 : VRLog( "Unable to determine VR Path Registry filename\n" );
217 0 : return false;
218 : }
219 :
220 0 : std::string sRegistryContents = Path_ReadTextFile( sRegPath );
221 0 : if( sRegistryContents.empty() )
222 : {
223 0 : VRLog( "Unable to read VR Path Registry from %s\n", sRegPath.c_str() );
224 0 : return false;
225 : }
226 :
227 0 : Json::Value root;
228 0 : Json::Reader reader;
229 :
230 0 : if( !reader.parse( sRegistryContents, root ) )
231 : {
232 0 : VRLog( "Unable to parse %s: %s\n", sRegPath.c_str(), reader.getFormattedErrorMessages().c_str() );
233 0 : return false;
234 : }
235 :
236 0 : ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" );
237 0 : ParseStringListFromJson( &m_vecConfigPath, root, "config" );
238 0 : ParseStringListFromJson( &m_vecLogPath, root, "log" );
239 0 : if (root.isMember( "external_drivers" ) && root[ "external_drivers" ].isArray() )
240 : {
241 0 : ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" );
242 : }
243 :
244 0 : return true;
245 : }
246 :
247 :
248 : // ---------------------------------------------------------------------------
249 : // Purpose: Saves the config file to its well known location
250 : // ---------------------------------------------------------------------------
251 0 : bool CVRPathRegistry_Public::BSaveToFile() const
252 : {
253 : #if defined( DASHBOARD_BUILD_MODE )
254 : return false;
255 : #else
256 0 : std::string sRegPath = GetVRPathRegistryFilename();
257 0 : if( sRegPath.empty() )
258 0 : return false;
259 :
260 0 : Json::Value root;
261 :
262 0 : root[ "version" ] = 1;
263 0 : root[ "jsonid" ] = "vrpathreg";
264 :
265 0 : StringListToJson( m_vecRuntimePath, root, "runtime" );
266 0 : StringListToJson( m_vecConfigPath, root, "config" );
267 0 : StringListToJson( m_vecLogPath, root, "log" );
268 0 : StringListToJson( m_vecExternalDrivers, root, "external_drivers" );
269 :
270 0 : Json::StyledWriter writer;
271 0 : std::string sRegistryContents = writer.write( root );
272 :
273 : // make sure the directory we're writing into actually exists
274 0 : std::string sRegDirectory = Path_StripFilename( sRegPath );
275 0 : if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) )
276 : {
277 0 : VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() );
278 0 : return false;
279 : }
280 :
281 0 : if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) )
282 : {
283 0 : VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() );
284 0 : return false;
285 : }
286 :
287 0 : return true;
288 : #endif
289 : }
290 :
291 :
292 : // ---------------------------------------------------------------------------
293 : // Purpose: Returns the current runtime path or NULL if no path is configured.
294 : // ---------------------------------------------------------------------------
295 0 : std::string CVRPathRegistry_Public::GetRuntimePath() const
296 : {
297 0 : if( m_vecRuntimePath.empty() )
298 0 : return "";
299 : else
300 0 : return m_vecRuntimePath.front().c_str();
301 : }
302 :
303 :
304 : // ---------------------------------------------------------------------------
305 : // Purpose: Returns the current config path or NULL if no path is configured.
306 : // ---------------------------------------------------------------------------
307 0 : std::string CVRPathRegistry_Public::GetConfigPath() const
308 : {
309 0 : if( m_vecConfigPath.empty() )
310 0 : return "";
311 : else
312 0 : return m_vecConfigPath.front().c_str();
313 : }
314 :
315 :
316 : // ---------------------------------------------------------------------------
317 : // Purpose: Returns the current log path or NULL if no path is configured.
318 : // ---------------------------------------------------------------------------
319 0 : std::string CVRPathRegistry_Public::GetLogPath() const
320 : {
321 0 : if( m_vecLogPath.empty() )
322 0 : return "";
323 : else
324 0 : return m_vecLogPath.front().c_str();
325 : }
326 :
327 :
328 :
329 : // ---------------------------------------------------------------------------
330 : // Purpose: Returns paths using the path registry and the provided override
331 : // values. Pass NULL for any paths you don't care about.
332 : // ---------------------------------------------------------------------------
333 0 : bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers )
334 : {
335 0 : CVRPathRegistry_Public pathReg;
336 0 : bool bLoadedRegistry = pathReg.BLoadFromFile();
337 0 : int nCountEnvironmentVariables = 0;
338 :
339 0 : if( psRuntimePath )
340 : {
341 0 : if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
342 : {
343 0 : *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
344 0 : nCountEnvironmentVariables++;
345 : }
346 0 : else if( !pathReg.GetRuntimePath().empty() )
347 : {
348 0 : *psRuntimePath = pathReg.GetRuntimePath();
349 : }
350 : else
351 : {
352 0 : *psRuntimePath = "";
353 : }
354 : }
355 :
356 0 : if( psConfigPath )
357 : {
358 0 : if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 )
359 : {
360 0 : *psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar );
361 0 : nCountEnvironmentVariables++;
362 : }
363 0 : else if( pchConfigPathOverride )
364 : {
365 0 : *psConfigPath = pchConfigPathOverride;
366 : }
367 0 : else if( !pathReg.GetConfigPath().empty() )
368 : {
369 0 : *psConfigPath = pathReg.GetConfigPath();
370 : }
371 : else
372 : {
373 0 : *psConfigPath = "";
374 : }
375 : }
376 :
377 0 : if( psLogPath )
378 : {
379 0 : if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 )
380 : {
381 0 : *psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar );
382 0 : nCountEnvironmentVariables++;
383 : }
384 0 : else if( pchLogPathOverride )
385 : {
386 0 : *psLogPath = pchLogPathOverride;
387 : }
388 0 : else if( !pathReg.GetLogPath().empty() )
389 : {
390 0 : *psLogPath = pathReg.GetLogPath();
391 : }
392 : else
393 : {
394 0 : *psLogPath = "";
395 : }
396 : }
397 :
398 0 : if ( pvecExternalDrivers )
399 : {
400 0 : *pvecExternalDrivers = pathReg.m_vecExternalDrivers;
401 : }
402 :
403 0 : if ( nCountEnvironmentVariables == 3 )
404 : {
405 : // all three environment variables where set, so we don't need the physical file
406 0 : return true;
407 : }
408 :
409 0 : return bLoadedRegistry;
410 : }
411 :
|