summaryrefslogtreecommitdiffstats
path: root/gfx/vr/service/openvr/src
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/vr/service/openvr/src')
-rw-r--r--gfx/vr/service/openvr/src/README39
-rw-r--r--gfx/vr/service/openvr/src/dirtools_public.cpp101
-rw-r--r--gfx/vr/service/openvr/src/dirtools_public.h17
-rw-r--r--gfx/vr/service/openvr/src/envvartools_public.cpp88
-rw-r--r--gfx/vr/service/openvr/src/envvartools_public.h8
-rw-r--r--gfx/vr/service/openvr/src/hmderrors_public.cpp326
-rw-r--r--gfx/vr/service/openvr/src/hmderrors_public.h6
-rw-r--r--gfx/vr/service/openvr/src/ivrclientcore.h35
-rw-r--r--gfx/vr/service/openvr/src/openvr_api_public.cpp352
-rw-r--r--gfx/vr/service/openvr/src/pathtools_public.cpp901
-rw-r--r--gfx/vr/service/openvr/src/pathtools_public.h150
-rw-r--r--gfx/vr/service/openvr/src/sharedlibtools_public.cpp43
-rw-r--r--gfx/vr/service/openvr/src/sharedlibtools_public.h10
-rw-r--r--gfx/vr/service/openvr/src/strtools_public.cpp571
-rw-r--r--gfx/vr/service/openvr/src/strtools_public.h156
-rw-r--r--gfx/vr/service/openvr/src/vrpathregistry_public.cpp442
-rw-r--r--gfx/vr/service/openvr/src/vrpathregistry_public.h45
17 files changed, 3290 insertions, 0 deletions
diff --git a/gfx/vr/service/openvr/src/README b/gfx/vr/service/openvr/src/README
new file mode 100644
index 0000000000..826c58da79
--- /dev/null
+++ b/gfx/vr/service/openvr/src/README
@@ -0,0 +1,39 @@
+This is the source code for the OpenVR API client binding library which connects
+OpenVR applications to the SteamVR runtime, taking into account the version
+of the OpenVR interface they were compiled against.
+
+The client binding library - openvr_api.dll on Windows, openvr_api.so on
+Linux, and openvr_api.dylib or OpenVR.framework on macOS - knows how to find
+and read the SteamVR runtime installation information which allows it to
+find and dynamically connect to the installed runtime. In combination with the
+interface version identifiers from /include/openvr.h which are baked
+into applications at the time they are built, the OpenVR API client
+binding library captures and conveys to the SteamVR runtime the version
+of the OpenVR API interface behavior that the application expects.
+
+Applications carry with them a private/local copy of the client binding
+library when they ship, and they should install it locally to their
+application. Applications should not install the client binding library
+globally or attempt to link to a globally installed client binding library.
+Doing so negates at least part of the ability for the client binding library
+to accurately reflect the version of the OpenVR API that the application
+was built against, and so hinders compatibility support in the face of
+API changes.
+
+Most applications should simply link to and redistribute with their application
+the pre-built client binding library found in the /bin directory of this
+repository. Some small number of applications which have specific requirements
+around redistributing only binaries they build themselves should build
+the client library from this source and either statically link it into
+their application or redistribute the binary they build.
+
+This is a cmake project, to build it use the version of cmake appropriate
+for your platform. For example, to build on a POSIX system simply perform
+
+ cd src; mkdir _build; cd _build; cmake ..; make
+
+and you will end up with the static library /src/bin/<arch>/libopenvr_api.a
+
+To build a shared library, pass -DBUILD_SHARED=1 to cmake.
+To build as a framework on apple platforms, pass -DBUILD_FRAMEWORK=1 to cmake.
+To see a complete list of configurable build options, use `cmake -LAH`
diff --git a/gfx/vr/service/openvr/src/dirtools_public.cpp b/gfx/vr/service/openvr/src/dirtools_public.cpp
new file mode 100644
index 0000000000..e5cfc02e0c
--- /dev/null
+++ b/gfx/vr/service/openvr/src/dirtools_public.cpp
@@ -0,0 +1,101 @@
+//========= Copyright Valve Corporation ============//
+#include "dirtools_public.h"
+#include "strtools_public.h"
+#include "pathtools_public.h"
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include "windows.h"
+#else
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#if defined( OSX )
+#include <sys/syslimits.h>
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: utility function to create dirs & subdirs
+//-----------------------------------------------------------------------------
+bool BCreateDirectoryRecursive( const char *pchPath )
+{
+ // Does it already exist?
+ if ( Path_IsDirectory( pchPath ) )
+ return true;
+
+ // copy the path into something we can munge
+ int len = (int)strlen( pchPath );
+ char *path = (char *)malloc( len + 1 );
+ strcpy( path, pchPath );
+
+ // Walk backwards to first non-existing dir that we find
+ char *s = path + len - 1;
+
+ const char slash = Path_GetSlash();
+ while ( s > path )
+ {
+ if ( *s == slash )
+ {
+ *s = '\0';
+ bool bExists = Path_IsDirectory( path );
+ *s = slash;
+
+ if ( bExists )
+ {
+ ++s;
+ break;
+ }
+ }
+ --s;
+ }
+
+ // and then move forwards from there
+
+ while ( *s )
+ {
+ if ( *s == slash )
+ {
+ *s = '\0';
+ BCreateDirectory( path );
+ *s = slash;
+ }
+ s++;
+ }
+
+ bool bRetVal = BCreateDirectory( path );
+ free( path );
+ return bRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the directory, returning true if it is created, or if it already existed
+//-----------------------------------------------------------------------------
+bool BCreateDirectory( const char *pchPath )
+{
+#ifdef WIN32
+ std::wstring wPath = UTF8to16( pchPath );
+ if ( ::CreateDirectoryW( wPath.c_str(), NULL ) )
+ return true;
+
+ if ( ::GetLastError() == ERROR_ALREADY_EXISTS )
+ return true;
+
+ return false;
+#else
+ int i = mkdir( pchPath, S_IRWXU | S_IRWXG | S_IRWXO );
+ if ( i == 0 )
+ return true;
+ if ( errno == EEXIST )
+ return true;
+
+ return false;
+#endif
+}
+
diff --git a/gfx/vr/service/openvr/src/dirtools_public.h b/gfx/vr/service/openvr/src/dirtools_public.h
new file mode 100644
index 0000000000..b048b41b78
--- /dev/null
+++ b/gfx/vr/service/openvr/src/dirtools_public.h
@@ -0,0 +1,17 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+
+#if !defined(_WIN32)
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+
+extern bool BCreateDirectoryRecursive( const char *pchPath );
+extern bool BCreateDirectory( const char *pchPath );
+
+
diff --git a/gfx/vr/service/openvr/src/envvartools_public.cpp b/gfx/vr/service/openvr/src/envvartools_public.cpp
new file mode 100644
index 0000000000..4fb4817927
--- /dev/null
+++ b/gfx/vr/service/openvr/src/envvartools_public.cpp
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation ============//
+#include "envvartools_public.h"
+#include "strtools_public.h"
+#include <stdlib.h>
+#include <string>
+#include <cctype>
+
+#if defined(_WIN32)
+#include <windows.h>
+
+#undef GetEnvironmentVariable
+#undef SetEnvironmentVariable
+#endif
+
+
+std::string GetEnvironmentVariable( const char *pchVarName )
+{
+#if defined(_WIN32)
+ char rchValue[32767]; // max size for an env var on Windows
+ DWORD cChars = GetEnvironmentVariableA( pchVarName, rchValue, sizeof( rchValue ) );
+ if( cChars == 0 )
+ return "";
+ else
+ return rchValue;
+#elif defined(POSIX)
+ char *pchValue = getenv( pchVarName );
+ if( pchValue )
+ return pchValue;
+ else
+ return "";
+#else
+#error "Unsupported Platform"
+#endif
+}
+
+bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault )
+{
+ std::string sValue = GetEnvironmentVariable( pchVarName );
+
+ if ( sValue.empty() )
+ {
+ return bDefault;
+ }
+
+ sValue = StringToLower( sValue );
+ std::string sYesValues[] = { "y", "yes", "true" };
+ std::string sNoValues[] = { "n", "no", "false" };
+
+ for ( std::string &sMatch : sYesValues )
+ {
+ if ( sMatch == sValue )
+ {
+ return true;
+ }
+ }
+
+ for ( std::string &sMatch : sNoValues )
+ {
+ if ( sMatch == sValue )
+ {
+ return false;
+ }
+ }
+
+ if ( std::isdigit( sValue.at(0) ) )
+ {
+ return atoi( sValue.c_str() ) != 0;
+ }
+
+ fprintf( stderr,
+ "GetEnvironmentVariableAsBool(%s): Unable to parse value '%s', using default %d\n",
+ pchVarName, sValue.c_str(), bDefault );
+ return bDefault;
+}
+
+bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue )
+{
+#if defined(_WIN32)
+ return 0 != SetEnvironmentVariableA( pchVarName, pchVarValue );
+#elif defined(POSIX)
+ if( pchVarValue == NULL )
+ return 0 == unsetenv( pchVarName );
+ else
+ return 0 == setenv( pchVarName, pchVarValue, 1 );
+#else
+#error "Unsupported Platform"
+#endif
+}
diff --git a/gfx/vr/service/openvr/src/envvartools_public.h b/gfx/vr/service/openvr/src/envvartools_public.h
new file mode 100644
index 0000000000..7cd4c208e6
--- /dev/null
+++ b/gfx/vr/service/openvr/src/envvartools_public.h
@@ -0,0 +1,8 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+
+std::string GetEnvironmentVariable( const char *pchVarName );
+bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault );
+bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue );
diff --git a/gfx/vr/service/openvr/src/hmderrors_public.cpp b/gfx/vr/service/openvr/src/hmderrors_public.cpp
new file mode 100644
index 0000000000..e02c370cb1
--- /dev/null
+++ b/gfx/vr/service/openvr/src/hmderrors_public.cpp
@@ -0,0 +1,326 @@
+//========= Copyright Valve Corporation ============//
+#include "openvr.h"
+#include "hmderrors_public.h"
+#include <stdio.h>
+#include <algorithm>
+
+using namespace vr;
+
+#define RETURN_ENUM_AS_STRING(enumValue) case enumValue: return #enumValue;
+
+
+const char *GetEnglishStringForHmdError( vr::EVRInitError eError )
+{
+ switch( eError )
+ {
+ case VRInitError_None: return "No Error (0)";
+
+ case VRInitError_Init_InstallationNotFound: return "Installation Not Found (100)";
+ case VRInitError_Init_InstallationCorrupt: return "Installation Corrupt (101)";
+ case VRInitError_Init_VRClientDLLNotFound: return "vrclient Shared Lib Not Found (102)";
+ case VRInitError_Init_FileNotFound: return "File Not Found (103)";
+ case VRInitError_Init_FactoryNotFound: return "Factory Function Not Found (104)";
+ case VRInitError_Init_InterfaceNotFound: return "Interface Not Found (105)";
+ case VRInitError_Init_InvalidInterface: return "Invalid Interface (106)";
+ case VRInitError_Init_UserConfigDirectoryInvalid: return "User Config Directory Invalid (107)";
+ case VRInitError_Init_HmdNotFound: return "Hmd Not Found (108)";
+ case VRInitError_Init_NotInitialized: return "Not Initialized (109)";
+ case VRInitError_Init_PathRegistryNotFound: return "Installation path could not be located (110)";
+ case VRInitError_Init_NoConfigPath: return "Config path could not be located (111)";
+ case VRInitError_Init_NoLogPath: return "Log path could not be located (112)";
+ case VRInitError_Init_PathRegistryNotWritable: return "Unable to write path registry (113)";
+ case VRInitError_Init_AppInfoInitFailed: return "App info manager init failed (114)";
+ case VRInitError_Init_Retry: return "Internal Retry (115)";
+ case VRInitError_Init_InitCanceledByUser: return "User Canceled Init (116)";
+ case VRInitError_Init_AnotherAppLaunching: return "Another app was already launching (117)";
+ case VRInitError_Init_SettingsInitFailed: return "Settings manager init failed (118)";
+ case VRInitError_Init_ShuttingDown: return "VR system shutting down (119)";
+ case VRInitError_Init_TooManyObjects: return "Too many tracked objects (120)";
+ case VRInitError_Init_NoServerForBackgroundApp: return "Not starting vrserver for background app (121)";
+ case VRInitError_Init_NotSupportedWithCompositor: return "The requested interface is incompatible with the compositor and the compositor is running (122)";
+ case VRInitError_Init_NotAvailableToUtilityApps: return "This interface is not available to utility applications (123)";
+ case VRInitError_Init_Internal: return "vrserver internal error (124)";
+ case VRInitError_Init_HmdDriverIdIsNone: return "Hmd DriverId is invalid (125)";
+ case VRInitError_Init_HmdNotFoundPresenceFailed: return "Hmd Not Found Presence Failed (126)";
+ case VRInitError_Init_VRMonitorNotFound: return "VR Monitor Not Found (127)";
+ case VRInitError_Init_VRMonitorStartupFailed: return "VR Monitor startup failed (128)";
+ case VRInitError_Init_LowPowerWatchdogNotSupported: return "Low Power Watchdog Not Supported (129)";
+ case VRInitError_Init_InvalidApplicationType: return "Invalid Application Type (130)";
+ case VRInitError_Init_NotAvailableToWatchdogApps: return "Not available to watchdog apps (131)";
+ case VRInitError_Init_WatchdogDisabledInSettings: return "Watchdog disabled in settings (132)";
+ case VRInitError_Init_VRDashboardNotFound: return "VR Dashboard Not Found (133)";
+ case VRInitError_Init_VRDashboardStartupFailed: return "VR Dashboard startup failed (134)";
+ case VRInitError_Init_VRHomeNotFound: return "VR Home Not Found (135)";
+ case VRInitError_Init_VRHomeStartupFailed: return "VR home startup failed (136)";
+ case VRInitError_Init_RebootingBusy: return "Rebooting In Progress (137)";
+ case VRInitError_Init_FirmwareUpdateBusy: return "Firmware Update In Progress (138)";
+ case VRInitError_Init_FirmwareRecoveryBusy: return "Firmware Recovery In Progress (139)";
+ case VRInitError_Init_USBServiceBusy: return "USB Service Busy (140)";
+
+ case VRInitError_Driver_Failed: return "Driver Failed (200)";
+ case VRInitError_Driver_Unknown: return "Driver Not Known (201)";
+ case VRInitError_Driver_HmdUnknown: return "HMD Not Known (202)";
+ case VRInitError_Driver_NotLoaded: return "Driver Not Loaded (203)";
+ case VRInitError_Driver_RuntimeOutOfDate: return "Driver runtime is out of date (204)";
+ case VRInitError_Driver_HmdInUse: return "HMD already in use by another application (205)";
+ case VRInitError_Driver_NotCalibrated: return "Device is not calibrated (206)";
+ case VRInitError_Driver_CalibrationInvalid: return "Device Calibration is invalid (207)";
+ case VRInitError_Driver_HmdDisplayNotFound: return "HMD detected over USB, but Monitor not found (208)";
+ case VRInitError_Driver_TrackedDeviceInterfaceUnknown: return "Driver Tracked Device Interface unknown (209)";
+ // case VRInitError_Driver_HmdDisplayNotFoundAfterFix: return "HMD detected over USB, but Monitor not found after attempt to fix (210)"; // taken out upon Ben's request: He thinks that there is no need to separate that error from 208
+ case VRInitError_Driver_HmdDriverIdOutOfBounds: return "Hmd DriverId is our of bounds (211)";
+ case VRInitError_Driver_HmdDisplayMirrored: return "HMD detected over USB, but Monitor may be mirrored instead of extended (212)";
+ case VRInitError_Driver_HmdDisplayNotFoundLaptop: return "On laptop, HMD detected over USB, but Monitor not found (213)";
+
+ case VRInitError_IPC_ServerInitFailed: return "VR Server Init Failed (300)";
+ case VRInitError_IPC_ConnectFailed: return "Connect to VR Server Failed (301)";
+ case VRInitError_IPC_SharedStateInitFailed: return "Shared IPC State Init Failed (302)";
+ case VRInitError_IPC_CompositorInitFailed: return "Shared IPC Compositor Init Failed (303)";
+ case VRInitError_IPC_MutexInitFailed: return "Shared IPC Mutex Init Failed (304)";
+ case VRInitError_IPC_Failed: return "Shared IPC Failed (305)";
+ case VRInitError_IPC_CompositorConnectFailed: return "Shared IPC Compositor Connect Failed (306)";
+ case VRInitError_IPC_CompositorInvalidConnectResponse: return "Shared IPC Compositor Invalid Connect Response (307)";
+ case VRInitError_IPC_ConnectFailedAfterMultipleAttempts: return "Shared IPC Connect Failed After Multiple Attempts (308)";
+ case VRInitError_IPC_ConnectFailedAfterTargetExited: return "Shared IPC Connect Failed After Target Exited (309)";
+ case VRInitError_IPC_NamespaceUnavailable: return "Shared IPC Namespace Unavailable (310)";
+
+ case VRInitError_Compositor_Failed: return "Compositor failed to initialize (400)";
+ case VRInitError_Compositor_D3D11HardwareRequired: return "Compositor failed to find DX11 hardware (401)";
+ case VRInitError_Compositor_FirmwareRequiresUpdate: return "Compositor requires mandatory firmware update (402)";
+ case VRInitError_Compositor_OverlayInitFailed: return "Compositor initialization succeeded, but overlay init failed (403)";
+ case VRInitError_Compositor_ScreenshotsInitFailed: return "Compositor initialization succeeded, but screenshot init failed (404)";
+ case VRInitError_Compositor_UnableToCreateDevice: return "Compositor unable to create graphics device (405)";
+
+ // Oculus
+ case VRInitError_VendorSpecific_UnableToConnectToOculusRuntime: return "Unable to connect to Oculus Runtime (1000)";
+ case VRInitError_VendorSpecific_OculusRuntimeBadInstall: return "Unable to connect to Oculus Runtime, possible bad install (1114)";
+
+ // Lighthouse
+ case VRInitError_VendorSpecific_HmdFound_CantOpenDevice: return "HMD found, but can not open device (1101)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart: return "HMD found, but unable to request config (1102)";
+ case VRInitError_VendorSpecific_HmdFound_NoStoredConfig: return "HMD found, but no stored config (1103)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck: return "HMD found, but failed configuration check (1113)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigTooBig: return "HMD found, but config too big (1104)";
+ case VRInitError_VendorSpecific_HmdFound_ConfigTooSmall: return "HMD found, but config too small (1105)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToInitZLib: return "HMD found, but unable to init ZLib (1106)";
+ case VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion: return "HMD found, but problems with the data (1107)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart: return "HMD found, but problems with the data (1108)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart: return "HMD found, but problems with the data (1109)";
+ case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext: return "HMD found, but problems with the data (1110)";
+ case VRInitError_VendorSpecific_HmdFound_UserDataAddressRange: return "HMD found, but problems with the data (1111)";
+ case VRInitError_VendorSpecific_HmdFound_UserDataError: return "HMD found, but problems with the data (1112)";
+
+ case VRInitError_Steam_SteamInstallationNotFound: return "Unable to find Steam installation (2000)";
+
+ default:
+ return GetIDForVRInitError( eError );
+ }
+
+}
+
+
+const char *GetIDForVRInitError( vr::EVRInitError eError )
+{
+ switch( eError )
+ {
+ RETURN_ENUM_AS_STRING( VRInitError_None );
+ RETURN_ENUM_AS_STRING( VRInitError_Unknown );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationCorrupt );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRClientDLLNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FileNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FactoryNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InterfaceNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidInterface );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_UserConfigDirectoryInvalid );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotInitialized );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoConfigPath );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoLogPath );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotWritable );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AppInfoInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_Retry );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InitCanceledByUser );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AnotherAppLaunching );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_SettingsInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_ShuttingDown );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_TooManyObjects );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NoServerForBackgroundApp );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotSupportedWithCompositor );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToUtilityApps );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_Internal );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdDriverIdIsNone );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFoundPresenceFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_LowPowerWatchdogNotSupported );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidApplicationType );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToWatchdogApps );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_WatchdogDisabledInSettings );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeNotFound );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_RebootingBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareUpdateBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareRecoveryBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_USBServiceBusy );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_VRWebHelperStartupFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_TrackerManagerInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_AlreadyRunning );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_FailedForVrMonitor);
+ RETURN_ENUM_AS_STRING( VRInitError_Init_PropertyManagerInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Init_WebServerFailed );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_Failed );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_Unknown );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdUnknown);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_NotLoaded);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_RuntimeOutOfDate);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdInUse);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_NotCalibrated);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_CalibrationInvalid);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFound);
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_TrackedDeviceInterfaceUnknown );
+ // RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundAfterFix );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDriverIdOutOfBounds );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayMirrored );
+ RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundLaptop );
+
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ServerInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_SharedStateInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_MutexInitFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_Failed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorConnectFailed);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInvalidConnectResponse);
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterMultipleAttempts );
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterTargetExited );
+ RETURN_ENUM_AS_STRING( VRInitError_IPC_NamespaceUnavailable );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_Failed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11HardwareRequired );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FirmwareRequiresUpdate );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_OverlayInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ScreenshotsInitFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_UnableToCreateDevice );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SharedStateIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_NotificationManagerIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ResourceManagerClientIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_MessageOverlaySharedStateInitFailure );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_PropertiesInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFullscreenWindowFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SettingsInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToShowWindow );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DistortInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayFrequencyFailure );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_RendererInitializationFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryInterfaceIsNull );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryCreateFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryQueryFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidAdapterDesktop );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdAttachment );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidOutputDesktop );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidDeviceProvided );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11RendererInitializationFailed );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToFindDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateSwapChain );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetBackBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateRenderTarget );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2SwapChain );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedtoGetDXGI2BackBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2RenderTarget );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetDXGIDeviceInterface );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_SelectDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateNvAPIRenderTargets );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_NvAPISetDisplayMode );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDirectModeDisplay );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdPropertyContainer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_UpdateDisplayFrequency );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateRasterizerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateWireframeRasterizerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateClampToBorderSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAnisoSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlaySamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanoramaSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFontSamplerState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateNoBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAlphaBlendState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskR );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskG );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskB );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilState );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoWrite );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoDepth );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFlushTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDistortionSurfaces );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseStagingConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSharedFrameInfoConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateReadableSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerGraphicsTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeSceneTextureIndexConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeHmdPoseConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateGeomConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanelMaskConstantBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePixelSimUBO );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMSAARenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateResolveRenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeResolveRenderTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDriverDirectModeResolveTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_OpenDriverDirectModeResolveTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFallbackSyncTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_ShareFallbackSyncTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayIndexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayVertexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextVertexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextIndexBuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorTextures );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLastFrameRenderTexture );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorOverlay );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateVirtualDisplayBackbuffer );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayModeNotSupported );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayInvalidCall );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayAlreadyInitialized );
+ RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateMailbox );
+
+ // Vendor-specific errors
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_UnableToConnectToOculusRuntime);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_WindowsNotInDevMode );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_OculusRuntimeBadInstall );
+
+ // Lighthouse
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantOpenDevice);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_NoStoredConfig);
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooBig );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooSmall );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToInitZLib );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataAddressRange );
+ RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataError );
+
+ RETURN_ENUM_AS_STRING( VRInitError_Steam_SteamInstallationNotFound );
+
+ default:
+ {
+ static char buf[128];
+ sprintf( buf, "Unknown error (%d)", eError );
+ return buf;
+ }
+ }
+}
+
diff --git a/gfx/vr/service/openvr/src/hmderrors_public.h b/gfx/vr/service/openvr/src/hmderrors_public.h
new file mode 100644
index 0000000000..ccd6c8a96c
--- /dev/null
+++ b/gfx/vr/service/openvr/src/hmderrors_public.h
@@ -0,0 +1,6 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+const char *GetEnglishStringForHmdError( vr::EVRInitError eError );
+const char *GetIDForVRInitError( vr::EVRInitError eError );
+
diff --git a/gfx/vr/service/openvr/src/ivrclientcore.h b/gfx/vr/service/openvr/src/ivrclientcore.h
new file mode 100644
index 0000000000..6884e7fbc8
--- /dev/null
+++ b/gfx/vr/service/openvr/src/ivrclientcore.h
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+namespace vr
+{
+
+class IVRClientCore
+{
+public:
+ /** Initializes the system */
+ virtual EVRInitError Init( vr::EVRApplicationType eApplicationType, const char *pStartupInfo ) = 0;
+
+ /** cleans up everything in vrclient.dll and prepares the DLL to be unloaded */
+ virtual void Cleanup() = 0;
+
+ /** checks to see if the specified interface/version is supported in this vrclient.dll */
+ virtual EVRInitError IsInterfaceVersionValid( const char *pchInterfaceVersion ) = 0;
+
+ /** Retrieves any interface from vrclient.dll */
+ virtual void *GetGenericInterface( const char *pchNameAndVersion, EVRInitError *peError ) = 0;
+
+ /** Returns true if any driver has an HMD attached. Can be called outside of Init/Cleanup */
+ virtual bool BIsHmdPresent() = 0;
+
+ /** Returns an English error string from inside vrclient.dll which might be newer than the API DLL */
+ virtual const char *GetEnglishStringForHmdError( vr::EVRInitError eError ) = 0;
+
+ /** Returns an error symbol from inside vrclient.dll which might be newer than the API DLL */
+ virtual const char *GetIDForVRInitError( vr::EVRInitError eError ) = 0;
+};
+
+static const char * const IVRClientCore_Version = "IVRClientCore_003";
+
+
+}
diff --git a/gfx/vr/service/openvr/src/openvr_api_public.cpp b/gfx/vr/service/openvr/src/openvr_api_public.cpp
new file mode 100644
index 0000000000..54aa555955
--- /dev/null
+++ b/gfx/vr/service/openvr/src/openvr_api_public.cpp
@@ -0,0 +1,352 @@
+//========= Copyright Valve Corporation ============//
+#define VR_API_EXPORT 1
+#include "openvr.h"
+#include "ivrclientcore.h"
+#include "pathtools_public.h"
+#include "sharedlibtools_public.h"
+#include "envvartools_public.h"
+#include "hmderrors_public.h"
+#include "strtools_public.h"
+#include "vrpathregistry_public.h"
+#include <mutex>
+
+using vr::EVRInitError;
+using vr::IVRSystem;
+using vr::IVRClientCore;
+using vr::VRInitError_None;
+
+// figure out how to import from the VR API dll
+#if defined(_WIN32)
+
+#if !defined(OPENVR_BUILD_STATIC)
+#define VR_EXPORT_INTERFACE extern "C" __declspec( dllexport )
+#else
+#define VR_EXPORT_INTERFACE extern "C"
+#endif
+
+#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)
+
+#define VR_EXPORT_INTERFACE extern "C" __attribute__((visibility("default")))
+
+#else
+#error "Unsupported Platform."
+#endif
+
+namespace vr
+{
+
+static void *g_pVRModule = NULL;
+static IVRClientCore *g_pHmdSystem = NULL;
+static std::recursive_mutex g_mutexSystem;
+
+
+typedef void* (*VRClientCoreFactoryFn)(const char *pInterfaceName, int *pReturnCode);
+
+static uint32_t g_nVRToken = 0;
+
+uint32_t VR_GetInitToken()
+{
+ return g_nVRToken;
+}
+
+EVRInitError VR_LoadHmdSystemInternal();
+void CleanupInternalInterfaces();
+
+
+uint32_t VR_InitInternal2( EVRInitError *peError, vr::EVRApplicationType eApplicationType, const char *pStartupInfo )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ EVRInitError err = VR_LoadHmdSystemInternal();
+ if ( err == vr::VRInitError_None )
+ {
+ err = g_pHmdSystem->Init( eApplicationType, pStartupInfo );
+ }
+
+ if ( peError )
+ *peError = err;
+
+ if ( err != VRInitError_None )
+ {
+ SharedLib_Unload( g_pVRModule );
+ g_pHmdSystem = NULL;
+ g_pVRModule = NULL;
+
+ return 0;
+ }
+
+ return ++g_nVRToken;
+}
+
+VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal( EVRInitError *peError, EVRApplicationType eApplicationType );
+
+uint32_t VR_InitInternal( EVRInitError *peError, vr::EVRApplicationType eApplicationType )
+{
+ return VR_InitInternal2( peError, eApplicationType, nullptr );
+}
+
+void VR_ShutdownInternal()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if ( g_pHmdSystem )
+ {
+ g_pHmdSystem->Cleanup();
+ g_pHmdSystem = NULL;
+ }
+ if ( g_pVRModule )
+ {
+ SharedLib_Unload( g_pVRModule );
+ g_pVRModule = NULL;
+ }
+
+#if !defined( VR_API_PUBLIC )
+ CleanupInternalInterfaces();
+#endif
+
+ ++g_nVRToken;
+}
+
+EVRInitError VR_LoadHmdSystemInternal()
+{
+ std::string sRuntimePath, sConfigPath, sLogPath;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
+ if( !bReadPathRegistry )
+ {
+ return vr::VRInitError_Init_PathRegistryNotFound;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return vr::VRInitError_Init_InstallationNotFound;
+ }
+
+ // Because we don't have a way to select debug vs. release yet we'll just
+ // use debug if it's there
+#if defined( LINUX64 ) || defined( LINUXARM64 )
+ std::string sTestPath = Path_Join( sRuntimePath, "bin", PLATSUBDIR );
+#else
+ std::string sTestPath = Path_Join( sRuntimePath, "bin" );
+#endif
+ if( !Path_IsDirectory( sTestPath ) )
+ {
+ return vr::VRInitError_Init_InstallationCorrupt;
+ }
+
+#if defined( WIN64 )
+ std::string sDLLPath = Path_Join( sTestPath, "vrclient_x64" DYNAMIC_LIB_EXT );
+#else
+ std::string sDLLPath = Path_Join( sTestPath, "vrclient" DYNAMIC_LIB_EXT );
+#endif
+
+ // only look in the override
+ void *pMod = SharedLib_Load( sDLLPath.c_str() );
+ // nothing more to do if we can't load the DLL
+ if( !pMod )
+ {
+ return vr::VRInitError_Init_VRClientDLLNotFound;
+ }
+
+ VRClientCoreFactoryFn fnFactory = ( VRClientCoreFactoryFn )( SharedLib_GetFunction( pMod, "VRClientCoreFactory" ) );
+ if( !fnFactory )
+ {
+ SharedLib_Unload( pMod );
+ return vr::VRInitError_Init_FactoryNotFound;
+ }
+
+ int nReturnCode = 0;
+ g_pHmdSystem = static_cast< IVRClientCore * > ( fnFactory( vr::IVRClientCore_Version, &nReturnCode ) );
+ if( !g_pHmdSystem )
+ {
+ SharedLib_Unload( pMod );
+ return vr::VRInitError_Init_InterfaceNotFound;
+ }
+
+ g_pVRModule = pMod;
+ return VRInitError_None;
+}
+
+
+void *VR_GetGenericInterface(const char *pchInterfaceVersion, EVRInitError *peError)
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if (!g_pHmdSystem)
+ {
+ if (peError)
+ *peError = vr::VRInitError_Init_NotInitialized;
+ return NULL;
+ }
+
+ return g_pHmdSystem->GetGenericInterface(pchInterfaceVersion, peError);
+}
+
+bool VR_IsInterfaceVersionValid(const char *pchInterfaceVersion)
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if (!g_pHmdSystem)
+ {
+ return false;
+ }
+
+ return g_pHmdSystem->IsInterfaceVersionValid(pchInterfaceVersion) == VRInitError_None;
+}
+
+bool VR_IsHmdPresent()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ {
+ // if we're already initialized, just call through
+ return g_pHmdSystem->BIsHmdPresent();
+ }
+ else
+ {
+ // otherwise we need to do a bit more work
+ EVRInitError err = VR_LoadHmdSystemInternal();
+ if( err != VRInitError_None )
+ return false;
+
+ bool bHasHmd = g_pHmdSystem->BIsHmdPresent();
+
+ g_pHmdSystem = NULL;
+ SharedLib_Unload( g_pVRModule );
+ g_pVRModule = NULL;
+
+ return bHasHmd;
+ }
+}
+
+/** Returns true if the OpenVR runtime is installed. */
+bool VR_IsRuntimeInstalled()
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ {
+ // if we're already initialized, OpenVR is obviously installed
+ return true;
+ }
+ else
+ {
+ // otherwise we need to do a bit more work
+ std::string sRuntimePath, sConfigPath, sLogPath;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
+ if( !bReadPathRegistry )
+ {
+ return false;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return false;
+ }
+
+ // the installation may be corrupt in some way, but it certainly looks installed
+ return true;
+ }
+}
+
+
+// -------------------------------------------------------------------------------
+// Purpose: This is the old Runtime Path interface that is no longer exported in the
+// latest header. We still want to export it from the DLL, though, so updating
+// to a new DLL doesn't break old compiled code. This version was not thread
+// safe and could change the buffer pointer to by a previous result on a
+// subsequent call
+// -------------------------------------------------------------------------------
+VR_EXPORT_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
+
+/** Returns where OpenVR runtime is installed. */
+const char *VR_RuntimePath()
+{
+ static char rchBuffer[1024];
+ uint32_t unRequiredSize;
+ if ( VR_GetRuntimePath( rchBuffer, sizeof( rchBuffer ), &unRequiredSize ) && unRequiredSize < sizeof( rchBuffer ) )
+ {
+ return rchBuffer;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+/** Returns where OpenVR runtime is installed. */
+bool VR_GetRuntimePath( char *pchPathBuffer, uint32_t unBufferSize, uint32_t *punRequiredBufferSize )
+{
+ // otherwise we need to do a bit more work
+ std::string sRuntimePath;
+
+ *punRequiredBufferSize = 0;
+
+ bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, nullptr, nullptr, nullptr, nullptr );
+ if ( !bReadPathRegistry )
+ {
+ return false;
+ }
+
+ // figure out where we're going to look for vrclient.dll
+ // see if the specified path actually exists.
+ if ( !Path_IsDirectory( sRuntimePath ) )
+ {
+ return false;
+ }
+
+ *punRequiredBufferSize = (uint32_t)sRuntimePath.size() + 1;
+ if ( sRuntimePath.size() >= unBufferSize )
+ {
+ *pchPathBuffer = '\0';
+ }
+ else
+ {
+ strcpy_safe( pchPathBuffer, unBufferSize, sRuntimePath.c_str() );
+ }
+
+ return true;
+}
+
+
+/** Returns the symbol version of an HMD error. */
+const char *VR_GetVRInitErrorAsSymbol( EVRInitError error )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if( g_pHmdSystem )
+ return g_pHmdSystem->GetIDForVRInitError( error );
+ else
+ return GetIDForVRInitError( error );
+}
+
+
+/** Returns the english string version of an HMD error. */
+const char *VR_GetVRInitErrorAsEnglishDescription( EVRInitError error )
+{
+ std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
+
+ if ( g_pHmdSystem )
+ return g_pHmdSystem->GetEnglishStringForHmdError( error );
+ else
+ return GetEnglishStringForHmdError( error );
+}
+
+
+VR_INTERFACE const char *VR_CALLTYPE VR_GetStringForHmdError( vr::EVRInitError error );
+
+/** Returns the english string version of an HMD error. */
+const char *VR_GetStringForHmdError( EVRInitError error )
+{
+ return VR_GetVRInitErrorAsEnglishDescription( error );
+}
+
+}
+
diff --git a/gfx/vr/service/openvr/src/pathtools_public.cpp b/gfx/vr/service/openvr/src/pathtools_public.cpp
new file mode 100644
index 0000000000..e7f6d6ca1b
--- /dev/null
+++ b/gfx/vr/service/openvr/src/pathtools_public.cpp
@@ -0,0 +1,901 @@
+//========= Copyright Valve Corporation ============//
+#include "strtools_public.h"
+#include "pathtools_public.h"
+
+#if defined( _WIN32)
+#include <windows.h>
+#include <direct.h>
+#include <shobjidl.h>
+#include <knownfolders.h>
+#include <shlobj.h>
+#include <share.h>
+
+#undef GetEnvironmentVariable
+#else
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <alloca.h>
+#endif
+
+#if defined OSX
+#include <Foundation/Foundation.h>
+#include <AppKit/AppKit.h>
+#include <mach-o/dyld.h>
+#define _S_IFDIR S_IFDIR // really from tier0/platform.h which we dont have yet
+#endif
+
+#include <sys/stat.h>
+
+#include <algorithm>
+
+/** Returns the path (including filename) to the current executable */
+std::string Path_GetExecutablePath()
+{
+#if defined( _WIN32 )
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[MAX_UNICODE_PATH_IN_UTF8];
+ ::GetModuleFileNameW( NULL, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete[] pchPath;
+ return sPath;
+#elif defined( OSX )
+ char rchPath[1024];
+ uint32_t nBuff = sizeof( rchPath );
+ bool bSuccess = _NSGetExecutablePath(rchPath, &nBuff) == 0;
+ rchPath[nBuff-1] = '\0';
+ if( bSuccess )
+ return rchPath;
+ else
+ return "";
+#elif defined LINUX
+ char rchPath[1024];
+ size_t nBuff = sizeof( rchPath );
+ ssize_t nRead = readlink("/proc/self/exe", rchPath, nBuff-1 );
+ if ( nRead != -1 )
+ {
+ rchPath[ nRead ] = 0;
+ return rchPath;
+ }
+ else
+ {
+ return "";
+ }
+#else
+ AssertMsg( false, "Implement Plat_GetExecutablePath" );
+ return "";
+#endif
+
+}
+
+/** Returns the path of the current working directory */
+std::string Path_GetWorkingDirectory()
+{
+ std::string sPath;
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ sPath = UTF16to8( _wgetcwd( buf, MAX_UNICODE_PATH ) );
+#else
+ char buf[ 1024 ];
+ sPath = getcwd( buf, sizeof( buf ) );
+#endif
+ return sPath;
+}
+
+/** Sets the path of the current working directory. Returns true if this was successful. */
+bool Path_SetWorkingDirectory( const std::string & sPath )
+{
+ bool bSuccess;
+#if defined( _WIN32 )
+ std::wstring wsPath = UTF8to16( sPath.c_str() );
+ bSuccess = 0 == _wchdir( wsPath.c_str() );
+#else
+ bSuccess = 0 == chdir( sPath.c_str() );
+#endif
+ return bSuccess;
+}
+
+/** Gets the path to a temporary directory. */
+std::string Path_GetTemporaryDirectory()
+{
+#if defined( _WIN32 )
+ wchar_t buf[MAX_UNICODE_PATH];
+ if ( GetTempPathW( MAX_UNICODE_PATH, buf ) == 0 )
+ return Path_GetWorkingDirectory();
+ return UTF16to8( buf );
+#else
+ const char *pchTmpDir = getenv( "TMPDIR" );
+ if ( pchTmpDir == NULL )
+ {
+ return "";
+ }
+ return pchTmpDir;
+#endif
+}
+
+/** Returns the specified path without its filename */
+std::string Path_StripFilename( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin(), sPath.begin() + n );
+}
+
+/** returns just the filename from the provided full or relative path. */
+std::string Path_StripDirectory( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string::size_type n = sPath.find_last_of( slash );
+ if( n == std::string::npos )
+ return sPath;
+ else
+ return std::string( sPath.begin() + n + 1, sPath.end() );
+}
+
+/** returns just the filename with no extension of the provided filename.
+* If there is a path the path is left intact. */
+std::string Path_StripExtension( const std::string & sPath )
+{
+ for( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if( *i == '.' )
+ {
+ return std::string( sPath.begin(), i.base() - 1 );
+ }
+
+ // if we find a slash there is no extension
+ if( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return sPath;
+}
+
+/** returns just extension of the provided filename (if any). */
+std::string Path_GetExtension( const std::string & sPath )
+{
+ for ( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
+ {
+ if ( *i == '.' )
+ {
+ return std::string( i.base(), sPath.end() );
+ }
+
+ // if we find a slash there is no extension
+ if ( *i == '\\' || *i == '/' )
+ break;
+ }
+
+ // we didn't find an extension
+ return "";
+}
+
+bool Path_IsAbsolute( const std::string & sPath )
+{
+ if( sPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ if ( sPath.size() < 3 ) // must be c:\x or \\x at least
+ return false;
+
+ if ( sPath[1] == ':' ) // drive letter plus slash, but must test both slash cases
+ {
+ if ( sPath[2] == '\\' || sPath[2] == '/' )
+ return true;
+ }
+ else if ( sPath[0] == '\\' && sPath[1] == '\\' ) // UNC path
+ return true;
+#else
+ if( sPath[0] == '\\' || sPath[0] == '/' ) // any leading slash
+ return true;
+#endif
+
+ return false;
+}
+
+
+/** Makes an absolute path from a relative path and a base path */
+std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath )
+{
+ if( Path_IsAbsolute( sRelativePath ) )
+ return Path_Compact( sRelativePath );
+ else
+ {
+ if( !Path_IsAbsolute( sBasePath ) )
+ return "";
+
+ std::string sCompacted = Path_Compact( Path_Join( sBasePath, sRelativePath ) );
+ if( Path_IsAbsolute( sCompacted ) )
+ return sCompacted;
+ else
+ return "";
+ }
+}
+
+
+/** Fixes the directory separators for the current platform */
+std::string Path_FixSlashes( const std::string & sPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sFixed = sPath;
+ for( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ )
+ {
+ if( *i == '/' || *i == '\\' )
+ *i = slash;
+ }
+
+ return sFixed;
+}
+
+
+char Path_GetSlash()
+{
+#if defined(_WIN32)
+ return '\\';
+#else
+ return '/';
+#endif
+}
+
+/** Jams two paths together with the right kind of slash */
+std::string Path_Join( const std::string & first, const std::string & second, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ // only insert a slash if we don't already have one
+ std::string::size_type nLen = first.length();
+ if( !nLen )
+ return second;
+#if defined(_WIN32)
+ if( first.back() == '\\' || first.back() == '/' )
+ nLen--;
+#else
+ char last_char = first[first.length()-1];
+ if (last_char == '\\' || last_char == '/')
+ nLen--;
+#endif
+
+ return first.substr( 0, nLen ) + std::string( 1, slash ) + second;
+}
+
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash )
+{
+ return Path_Join( Path_Join( first, second, slash ), third, slash );
+}
+
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash )
+{
+ return Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash );
+}
+
+std::string Path_Join(
+ const std::string & first,
+ const std::string & second,
+ const std::string & third,
+ const std::string & fourth,
+ const std::string & fifth,
+ char slash )
+{
+ return Path_Join( Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ), fifth, slash );
+}
+
+
+std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash )
+{
+ if ( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = sRawPath;
+ std::string::size_type nCurrent = sRawPath.length();
+ if ( nCurrent == 0 )
+ return sPath;
+
+ int nLastFound = -1;
+ nCurrent--;
+ while( nCurrent != 0 )
+ {
+ if ( sRawPath[ nCurrent ] == slash )
+ {
+ nLastFound = (int)nCurrent;
+ nCurrent--;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ( nLastFound >= 0 )
+ {
+ sPath.erase( nLastFound, std::string::npos );
+ }
+
+ return sPath;
+}
+
+
+/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
+* specified path has a broken number of directories for its number of ..s */
+std::string Path_Compact( const std::string & sRawPath, char slash )
+{
+ if( slash == 0 )
+ slash = Path_GetSlash();
+
+ std::string sPath = Path_FixSlashes( sRawPath, slash );
+ std::string sSlashString( 1, slash );
+
+ // strip out all /./
+ for( std::string::size_type i = 0; (i + 3) < sPath.length(); )
+ {
+ if( sPath[ i ] == slash && sPath[ i+1 ] == '.' && sPath[ i+2 ] == slash )
+ {
+ sPath.replace( i, 3, sSlashString );
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+
+ // get rid of trailing /. but leave the path separator
+ if( sPath.length() > 2 )
+ {
+ std::string::size_type len = sPath.length();
+ if( sPath[ len-1 ] == '.' && sPath[ len-2 ] == slash )
+ {
+ sPath.pop_back();
+ //Not sure why the following line of code was used for a while. It causes problems with strlen.
+ //sPath[len-1] = 0; // for now, at least
+ }
+ }
+
+ // get rid of leading ./
+ if( sPath.length() > 2 )
+ {
+ if( sPath[ 0 ] == '.' && sPath[ 1 ] == slash )
+ {
+ sPath.replace( 0, 2, "" );
+ }
+ }
+
+ // each time we encounter .. back up until we've found the previous directory name
+ // then get rid of both
+ std::string::size_type i = 0;
+ while( i < sPath.length() )
+ {
+ if( i > 0 && sPath.length() - i >= 2
+ && sPath[i] == '.'
+ && sPath[i+1] == '.'
+ && ( i + 2 == sPath.length() || sPath[ i+2 ] == slash )
+ && sPath[ i-1 ] == slash )
+ {
+ // check if we've hit the start of the string and have a bogus path
+ if( i == 1 )
+ return "";
+
+ // find the separator before i-1
+ std::string::size_type iDirStart = i-2;
+ while( iDirStart > 0 && sPath[ iDirStart - 1 ] != slash )
+ --iDirStart;
+
+ // remove everything from iDirStart to i+2
+ sPath.replace( iDirStart, (i - iDirStart) + 3, "" );
+
+ // start over
+ i = 0;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ return sPath;
+}
+
+
+/** Returns true if these two paths are the same without respect for internal . or ..
+* sequences, slash type, or case (on case-insensitive platforms). */
+bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 )
+{
+ std::string sCompact1 = Path_Compact( sPath1 );
+ std::string sCompact2 = Path_Compact( sPath2 );
+#if defined(WIN32)
+ return !stricmp( sCompact1.c_str(), sCompact2.c_str() );
+#else
+ return !strcmp( sCompact1.c_str(), sCompact2.c_str() );
+#endif
+}
+
+
+/** Returns the path to the current DLL or exe */
+std::string Path_GetThisModulePath()
+{
+ // gets the path of vrclient.dll itself
+#ifdef WIN32
+ HMODULE hmodule = NULL;
+
+ ::GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCTSTR>(Path_GetThisModulePath), &hmodule );
+
+ wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
+ char *pchPath = new char[ MAX_UNICODE_PATH_IN_UTF8 ];
+ ::GetModuleFileNameW( hmodule, pwchPath, MAX_UNICODE_PATH );
+ WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
+ delete[] pwchPath;
+
+ std::string sPath = pchPath;
+ delete [] pchPath;
+ return sPath;
+
+#elif defined( OSX ) || defined( LINUX )
+ // get the addr of a function in vrclient.so and then ask the dlopen system about it
+ Dl_info info;
+ dladdr( (void *)Path_GetThisModulePath, &info );
+ return info.dli_fname;
+#endif
+
+}
+
+
+/** returns true if the specified path exists and is a directory */
+bool Path_IsDirectory( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+ char cLast = sFixedPath[ sFixedPath.length() - 1 ];
+ if( cLast == '/' || cLast == '\\' )
+ sFixedPath.erase( sFixedPath.end() - 1, sFixedPath.end() );
+
+ // see if the specified path actually exists.
+
+#if defined(POSIX)
+ struct stat buf;
+ if ( stat( sFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+#if defined( LINUX ) || defined( OSX )
+ return S_ISDIR( buf.st_mode );
+#else
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+
+#else
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+
+ return (buf.st_mode & _S_IFDIR) != 0;
+#endif
+}
+
+/** returns true if the specified path represents an app bundle */
+bool Path_IsAppBundle( const std::string & sPath )
+{
+#if defined(OSX)
+ @autoreleasepool {
+ NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ];
+ bool bisAppBundle = ( nullptr != bundle );
+ return bisAppBundle;
+ }
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the the path exists
+//-----------------------------------------------------------------------------
+bool Path_Exists( const std::string & sPath )
+{
+ std::string sFixedPath = Path_FixSlashes( sPath );
+ if( sFixedPath.empty() )
+ return false;
+
+#if defined( WIN32 )
+ struct _stat buf;
+ std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
+ if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
+ {
+ return false;
+ }
+#else
+ struct stat buf;
+ if ( stat ( sFixedPath.c_str(), &buf ) == -1)
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a directory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ std::string strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+ if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
+ return strCurrentPath;
+ }
+
+ return "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper to find a subdirectory upstream from a given path
+//-----------------------------------------------------------------------------
+std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
+{
+ std::string strFoundPath = "";
+ std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
+ if ( strCurrentPath.length() == 0 )
+ return "";
+
+ bool bExists = Path_Exists( strCurrentPath );
+ while( bExists && strCurrentPath.length() != 0 )
+ {
+ strCurrentPath = Path_StripFilename( strCurrentPath );
+ bExists = Path_Exists( strCurrentPath );
+
+ if( Path_Exists( Path_Join( strCurrentPath, strDirectoryName ) ) )
+ {
+ strFoundPath = Path_Join( strCurrentPath, strDirectoryName );
+ break;
+ }
+ }
+ return strFoundPath;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: reading and writing files in the vortex directory
+//-----------------------------------------------------------------------------
+unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ // the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s
+ f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO );
+#endif
+
+ unsigned char* buf = NULL;
+
+ if ( f != NULL )
+ {
+ fseek(f, 0, SEEK_END);
+ int size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = new unsigned char[size];
+ if (buf && fread(buf, size, 1, f) == 1)
+ {
+ if (pSize)
+ *pSize = size;
+ }
+ else
+ {
+ delete[] buf;
+ buf = 0;
+ }
+
+ fclose(f);
+ }
+
+ return buf;
+}
+
+uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "rb" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"rb" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ uint32_t unSizeToReturn = 0;
+
+ if ( f != NULL )
+ {
+ fseek( f, 0, SEEK_END );
+ uint32_t size = (uint32_t)ftell( f );
+ fseek( f, 0, SEEK_SET );
+
+ if ( size > unSize || !pBuffer )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ else
+ {
+ if ( fread( pBuffer, size, 1, f ) == 1 )
+ {
+ unSizeToReturn = (uint32_t)size;
+ }
+ }
+
+ fclose( f );
+ }
+
+ return unSizeToReturn;
+}
+
+bool Path_WriteBinaryFile(const std::string &strFilename, unsigned char *pData, unsigned nSize)
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen(strFilename.c_str(), "wb");
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"wb" );
+ if (err != 0)
+ {
+ f = NULL;
+ }
+#endif
+
+ size_t written = 0;
+ if (f != NULL) {
+ written = fwrite(pData, sizeof(unsigned char), nSize, f);
+ fclose(f);
+ }
+
+ return written == nSize ? true : false;
+}
+
+std::string Path_ReadTextFile( const std::string &strFilename )
+{
+ // doing it this way seems backwards, but I don't
+ // see an easy way to do this with C/C++ style IO
+ // that isn't worse...
+ int size;
+ unsigned char* buf = Path_ReadBinaryFile( strFilename, &size );
+ if (!buf)
+ return "";
+
+ // convert CRLF -> LF
+ size_t outsize = 1;
+ for (int i=1; i < size; i++)
+ {
+ if (buf[i] == '\n' && buf[i-1] == '\r') // CRLF
+ buf[outsize-1] = '\n'; // ->LF
+ else
+ buf[outsize++] = buf[i]; // just copy
+ }
+
+ std::string ret((char *)buf, outsize);
+ delete[] buf;
+ return ret;
+}
+
+
+bool Path_MakeWritable( const std::string &strFilename )
+{
+#if defined ( _WIN32 )
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+
+ DWORD dwAttrs = GetFileAttributesW( wstrFilename.c_str() );
+ if ( dwAttrs != INVALID_FILE_ATTRIBUTES && ( dwAttrs & FILE_ATTRIBUTE_READONLY ) )
+ {
+ return SetFileAttributesW( wstrFilename.c_str(), dwAttrs & ~FILE_ATTRIBUTE_READONLY );
+ }
+#else
+ struct stat sb;
+
+ if ( stat( strFilename.c_str(), &sb ) == 0 && !( sb.st_mode & S_IWUSR ) )
+ {
+ return ( chmod( strFilename.c_str(), sb.st_mode | S_IWUSR ) == 0 );
+ }
+#endif
+
+ return true;
+}
+
+bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData )
+{
+ FILE *f;
+#if defined( POSIX )
+ f = fopen( strFilename.c_str(), "w" );
+#else
+ std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
+ errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"w" );
+ if ( err != 0 )
+ {
+ f = NULL;
+ }
+#endif
+
+ bool ok = false;
+
+ if ( f != NULL )
+ {
+ ok = fputs( pchData, f) >= 0;
+ fclose(f);
+ }
+
+ return ok;
+}
+
+bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData )
+{
+ std::string strTmpFilename = strFilename + ".tmp";
+
+ if ( !Path_WriteStringToTextFile( strTmpFilename, pchData ) )
+ return false;
+
+ // Platform specific atomic file replacement
+#if defined( _WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ std::wstring wsTmpFilename = UTF8to16( strTmpFilename.c_str() );
+ if ( !::ReplaceFileW( wsFilename.c_str(), wsTmpFilename.c_str(), nullptr, 0, 0, 0 ) )
+ {
+ // if we couldn't ReplaceFile, try a non-atomic write as a fallback
+ if ( !Path_WriteStringToTextFile( strFilename, pchData ) )
+ return false;
+ }
+#elif defined( POSIX )
+ if ( rename( strTmpFilename.c_str(), strFilename.c_str() ) == -1 )
+ return false;
+#else
+#error Do not know how to write atomic file
+#endif
+
+ return true;
+}
+
+
+#if defined(WIN32)
+#define FILE_URL_PREFIX "file:///"
+#else
+#define FILE_URL_PREFIX "file://"
+#endif
+
+// Mozilla: see README.mozilla for more details
+// ----------------------------------------------------------------------------------------------------------------------------
+// Purpose: Turns a path to a file on disk into a URL (or just returns the value if it's already a URL)
+// ----------------------------------------------------------------------------------------------------------------------------
+// std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath )
+// {
+// if ( StringHasPrefix( sRelativePath, "http://" )
+// || StringHasPrefix( sRelativePath, "https://" )
+// || StringHasPrefix( sRelativePath, "vr-input-workshop://" )
+// || StringHasPrefix( sRelativePath, "file://" )
+// )
+// {
+// return sRelativePath;
+// }
+// else
+// {
+// std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath );
+// if ( sAbsolute.empty() )
+// return sAbsolute;
+// sAbsolute = Path_FixSlashes( sAbsolute, '/' );
+
+// size_t unBufferSize = sAbsolute.length() * 3;
+// char *pchBuffer = (char *)alloca( unBufferSize );
+// V_URLEncodeFullPath( pchBuffer, (int)unBufferSize, sAbsolute.c_str(), (int)sAbsolute.length() );
+
+// return std::string( FILE_URL_PREFIX ) + pchBuffer;
+// }
+// }
+
+// Mozilla: see README.mozilla for more details
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned
+// -----------------------------------------------------------------------------------------------------
+// std::string Path_UrlToFilePath( const std::string & sFileUrl )
+// {
+// if ( !strnicmp( sFileUrl.c_str(), FILE_URL_PREFIX, strlen( FILE_URL_PREFIX ) ) )
+// {
+// char *pchBuffer = (char *)alloca( sFileUrl.length() );
+// V_URLDecodeNoPlusForSpace( pchBuffer, (int)sFileUrl.length(),
+// sFileUrl.c_str() + strlen( FILE_URL_PREFIX ), (int)( sFileUrl.length() - strlen( FILE_URL_PREFIX ) ) );
+
+// return Path_FixSlashes( pchBuffer );
+// }
+// else
+// {
+// return "";
+// }
+// }
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: Returns the root of the directory the system wants us to store user documents in
+// -----------------------------------------------------------------------------------------------------
+std::string GetUserDocumentsPath()
+{
+#if defined( WIN32 )
+ WCHAR rwchPath[MAX_PATH];
+
+ if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | 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 )
+ @autoreleasepool {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
+ if ( [paths count] == 0 )
+ {
+ return "";
+ }
+
+ return [[paths objectAtIndex:0] UTF8String];
+ }
+#elif defined( LINUX )
+ // @todo: not solved/changed as part of OSX - still not real - just removed old class based steam cut and paste
+ const char *pchHome = getenv( "HOME" );
+ if ( pchHome == NULL )
+ {
+ return "";
+ }
+ return pchHome;
+#endif
+}
+
+
+// -----------------------------------------------------------------------------------------------------
+// Purpose: deletes / unlinks a single file
+// -----------------------------------------------------------------------------------------------------
+bool Path_UnlinkFile( const std::string &strFilename )
+{
+#if defined( WIN32 )
+ std::wstring wsFilename = UTF8to16( strFilename.c_str() );
+ return ( 0 != DeleteFileW( wsFilename.c_str() ) );
+#else
+ return ( 0 == unlink( strFilename.c_str() ) );
+#endif
+}
diff --git a/gfx/vr/service/openvr/src/pathtools_public.h b/gfx/vr/service/openvr/src/pathtools_public.h
new file mode 100644
index 0000000000..33688fba2f
--- /dev/null
+++ b/gfx/vr/service/openvr/src/pathtools_public.h
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <stdint.h>
+
+/** Returns the path (including filename) to the current executable */
+std::string Path_GetExecutablePath();
+
+/** Returns the path of the current working directory */
+std::string Path_GetWorkingDirectory();
+
+/** Sets the path of the current working directory. Returns true if this was successful. */
+bool Path_SetWorkingDirectory( const std::string & sPath );
+
+/** Gets the path to a temporary directory. */
+std::string Path_GetTemporaryDirectory();
+
+/** returns the path (including filename) of the current shared lib or DLL */
+std::string Path_GetThisModulePath();
+
+/** Returns the specified path without its filename.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_StripFilename( const std::string & sPath, char slash = 0 );
+
+/** returns just the filename from the provided full or relative path. */
+std::string Path_StripDirectory( const std::string & sPath, char slash = 0 );
+
+/** returns just the filename with no extension of the provided filename.
+* If there is a path the path is left intact. */
+std::string Path_StripExtension( const std::string & sPath );
+
+/** returns just extension of the provided filename (if any). */
+std::string Path_GetExtension( const std::string & sPath );
+
+/** Returns true if the path is absolute */
+bool Path_IsAbsolute( const std::string & sPath );
+
+/** Makes an absolute path from a relative path and a base path */
+std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath );
+
+/** Fixes the directory separators for the current platform.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_FixSlashes( const std::string & sPath, char slash = 0 );
+
+/** Returns the path separator for the current platform */
+char Path_GetSlash();
+
+/** Jams two paths together with the right kind of slash */
+std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 );
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash = 0 );
+std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash = 0 );
+std::string Path_Join(
+ const std::string & first,
+ const std::string & second,
+ const std::string & third,
+ const std::string & fourth,
+ const std::string & fifth,
+ char slash = 0 );
+
+
+/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
+* specified path has a broken number of directories for its number of ..s.
+* If slash is unspecified the native path separator of the current platform
+* will be used. */
+std::string Path_Compact( const std::string & sRawPath, char slash = 0 );
+
+/** Returns true if these two paths are the same without respect for internal . or ..
+* sequences, slash type, or case (on case-insensitive platforms). */
+bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 );
+
+//** Removed trailing slashes */
+std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash = 0 );
+
+/** returns true if the specified path exists and is a directory */
+bool Path_IsDirectory( const std::string & sPath );
+
+/** returns true if the specified path represents an app bundle */
+bool Path_IsAppBundle( const std::string & sPath );
+
+/** returns true if the the path exists */
+bool Path_Exists( const std::string & sPath );
+
+/** Helper functions to find parent directories or subdirectories of parent directories */
+std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
+std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
+
+/** Make a text file writable. */
+bool Path_MakeWritable( const std::string &strFilename );
+
+/** Path operations to read or write text/binary files */
+unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize );
+uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize );
+bool Path_WriteBinaryFile( const std::string &strFilename, unsigned char *pData, unsigned nSize );
+std::string Path_ReadTextFile( const std::string &strFilename );
+bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData );
+bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData );
+
+// Mozilla: see README.mozilla for more details
+/** Returns a file:// url for paths, or an http or https url if that's what was provided */
+// std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath );
+
+/** Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned */
+std::string Path_UrlToFilePath( const std::string & sFileUrl );
+
+/** Returns the root of the directory the system wants us to store user documents in */
+std::string GetUserDocumentsPath();
+
+/** deletes / unlinks a single file */
+bool Path_UnlinkFile( const std::string &strFilename );
+
+#ifndef MAX_UNICODE_PATH
+ #define MAX_UNICODE_PATH 32767
+#endif
+
+#ifndef MAX_UNICODE_PATH_IN_UTF8
+ #define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH * 4)
+#endif
+
+//-----------------------------------------------------------------------------
+#if defined(WIN32)
+#define DYNAMIC_LIB_EXT ".dll"
+#define PROGRAM_EXT ".exe"
+#ifdef _WIN64
+#define PLATSUBDIR "win64"
+#else
+#define PLATSUBDIR "win32"
+#endif
+#elif defined(OSX)
+#define DYNAMIC_LIB_EXT ".dylib"
+#define PLATSUBDIR "osx32"
+#define PROGRAM_EXT ""
+#elif defined(LINUX)
+#define DYNAMIC_LIB_EXT ".so"
+#define PROGRAM_EXT ""
+#if defined( LINUX32 )
+#define PLATSUBDIR "linux32"
+#elif defined( ANDROIDARM64 )
+#define PLATSUBDIR "androidarm64"
+#elif defined( LINUXARM64 )
+#define PLATSUBDIR "linuxarm64"
+#else
+#define PLATSUBDIR "linux64"
+#endif
+#else
+#warning "Unknown platform for PLATSUBDIR"
+#define PLATSUBDIR "unknown_platform"
+#endif
diff --git a/gfx/vr/service/openvr/src/sharedlibtools_public.cpp b/gfx/vr/service/openvr/src/sharedlibtools_public.cpp
new file mode 100644
index 0000000000..048512a1de
--- /dev/null
+++ b/gfx/vr/service/openvr/src/sharedlibtools_public.cpp
@@ -0,0 +1,43 @@
+//========= Copyright Valve Corporation ============//
+#include "sharedlibtools_public.h"
+#include <string.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#if defined(POSIX)
+#include <dlfcn.h>
+#endif
+
+SharedLibHandle SharedLib_Load( const char *pchPath )
+{
+#if defined( _WIN32)
+ return (SharedLibHandle)LoadLibraryEx( pchPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
+#elif defined(POSIX)
+ return (SharedLibHandle)dlopen(pchPath, RTLD_LOCAL|RTLD_NOW);
+#endif
+}
+
+void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName)
+{
+#if defined( _WIN32)
+ return (void*)GetProcAddress( (HMODULE)lib, pchFunctionName );
+#elif defined(POSIX)
+ return dlsym( lib, pchFunctionName );
+#endif
+}
+
+
+void SharedLib_Unload( SharedLibHandle lib )
+{
+ if ( !lib )
+ return;
+#if defined( _WIN32)
+ FreeLibrary( (HMODULE)lib );
+#elif defined(POSIX)
+ dlclose( lib );
+#endif
+}
+
+
diff --git a/gfx/vr/service/openvr/src/sharedlibtools_public.h b/gfx/vr/service/openvr/src/sharedlibtools_public.h
new file mode 100644
index 0000000000..10163dbfab
--- /dev/null
+++ b/gfx/vr/service/openvr/src/sharedlibtools_public.h
@@ -0,0 +1,10 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+typedef void *SharedLibHandle;
+
+SharedLibHandle SharedLib_Load( const char *pchPath );
+void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName);
+void SharedLib_Unload( SharedLibHandle lib );
+
+
diff --git a/gfx/vr/service/openvr/src/strtools_public.cpp b/gfx/vr/service/openvr/src/strtools_public.cpp
new file mode 100644
index 0000000000..f52f8e9004
--- /dev/null
+++ b/gfx/vr/service/openvr/src/strtools_public.cpp
@@ -0,0 +1,571 @@
+//========= Copyright Valve Corporation ============//
+#include "strtools_public.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+// Mozilla: see README.mozilla for more details
+// #include <codecvt>
+#include <iostream>
+#include <functional>
+#include <locale>
+// #include <codecvt>
+
+#if defined( _WIN32 )
+#include <windows.h>
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool StringHasPrefix( const std::string & sString, const std::string & sPrefix )
+{
+ return 0 == strnicmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
+}
+
+bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix )
+{
+ return 0 == strncmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
+}
+
+
+bool StringHasSuffix( const std::string &sString, const std::string &sSuffix )
+{
+ size_t cStrLen = sString.length();
+ size_t cSuffixLen = sSuffix.length();
+
+ if ( cSuffixLen > cStrLen )
+ return false;
+
+ std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
+
+ return 0 == stricmp( sStringSuffix.c_str(), sSuffix.c_str() );
+}
+
+bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix )
+{
+ size_t cStrLen = sString.length();
+ size_t cSuffixLen = sSuffix.length();
+
+ if ( cSuffixLen > cStrLen )
+ return false;
+
+ std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
+
+ return 0 == strncmp( sStringSuffix.c_str(), sSuffix.c_str(),cSuffixLen );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+// Mozilla: see README.mozilla for more details
+//typedef std::codecvt_utf8< wchar_t > convert_type;
+
+// Mozilla: see README.mozilla for more details
+#if defined( _WIN32 )
+std::string UTF16to8(const wchar_t * in)
+{
+ int retLength = ::WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
+ if (retLength == 0)
+ {
+ return std::string();
+ }
+
+ char* retString = new char[retLength];
+ ::WideCharToMultiByte(CP_UTF8, 0, in, -1, retString, retLength, nullptr, nullptr);
+
+ std::string retStringValue(retString);
+
+ delete[] retString;
+
+ return retStringValue;
+
+ // static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
+
+ // try
+ // {
+ // return s_converter.to_bytes( in );
+ // }
+ // catch ( ... )
+ // {
+ // return std::string();
+ // }
+}
+
+std::string UTF16to8( const std::wstring & in ) { return UTF16to8( in.c_str() ); }
+
+// Mozilla: see README.mozilla for more details
+std::wstring UTF8to16(const char * in)
+{
+ int retLength = ::MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
+ if (retLength == 0)
+ {
+ return std::wstring();
+ }
+
+ wchar_t* retString = new wchar_t[retLength];
+ ::MultiByteToWideChar(CP_UTF8, 0, in, -1, retString, retLength);
+
+ std::wstring retStringValue(retString);
+
+ delete[] retString;
+
+ return retStringValue;
+
+ //static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
+
+ //try
+ //{
+ // return s_converter.from_bytes( in );
+ //}
+ //catch ( ... )
+ //{
+ // return std::wstring();
+ //}
+}
+
+std::wstring UTF8to16( const std::string & in ) { return UTF8to16( in.c_str() ); }
+#endif
+
+
+#if defined( _WIN32 )
+//-----------------------------------------------------------------------------
+// Purpose: Convert LPSTR in the default CodePage to UTF8
+//-----------------------------------------------------------------------------
+std::string DefaultACPtoUTF8( const char *pszStr )
+{
+ if ( GetACP() == CP_UTF8 )
+ {
+ return pszStr;
+ }
+ else
+ {
+ std::vector<wchar_t> vecBuf( strlen( pszStr ) + 1 ); // should be guaranteed to be enough
+ MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszStr, -1, vecBuf.data(), (int) vecBuf.size() );
+ return UTF16to8( vecBuf.data() );
+ }
+}
+#endif
+
+// --------------------------------------------------------------------
+// Purpose:
+// --------------------------------------------------------------------
+void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource )
+{
+ strncpy( pchBuffer, pchSource, unBufferSizeBytes - 1 );
+ pchBuffer[unBufferSizeBytes - 1] = '\0';
+}
+
+// --------------------------------------------------------------------
+// Purpose: converts a string to upper case
+// --------------------------------------------------------------------
+std::string StringToUpper( const std::string & sString )
+{
+ std::string sOut;
+ sOut.reserve( sString.size() + 1 );
+ for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
+ {
+ sOut.push_back( (char)toupper( *i ) );
+ }
+
+ return sOut;
+}
+
+
+// --------------------------------------------------------------------
+// Purpose: converts a string to lower case
+// --------------------------------------------------------------------
+std::string StringToLower( const std::string & sString )
+{
+ std::string sOut;
+ sOut.reserve( sString.size() + 1 );
+ for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
+ {
+ sOut.push_back( (char)tolower( *i ) );
+ }
+
+ return sOut;
+}
+
+
+uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen )
+{
+ uint32_t unLen = (uint32_t)sValue.length() + 1;
+ if( !pchBuffer || !unBufferLen )
+ return unLen;
+
+ if( unBufferLen < unLen )
+ {
+ pchBuffer[0] = '\0';
+ }
+ else
+ {
+ memcpy( pchBuffer, sValue.c_str(), unLen );
+ }
+
+ return unLen;
+}
+
+
+/** Returns a std::string from a uint64_t */
+// Mozilla: see README.mozilla for more details
+// std::string Uint64ToString( uint64_t ulValue )
+// {
+// char buf[ 22 ];
+// #if defined( _WIN32 )
+// sprintf_s( buf, "%llu", ulValue );
+// #else
+// snprintf( buf, sizeof( buf ), "%llu", (long long unsigned int ) ulValue );
+// #endif
+// return buf;
+// }
+
+
+/** returns a uint64_t from a string */
+uint64_t StringToUint64( const std::string & sValue )
+{
+ return strtoull( sValue.c_str(), NULL, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for converting a numeric value to a hex digit, value should be 0-15.
+//-----------------------------------------------------------------------------
+char cIntToHexDigit( int nValue )
+{
+ //Assert( nValue >= 0 && nValue <= 15 );
+ return "0123456789ABCDEF"[ nValue & 15 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for converting a hex char value to numeric, return -1 if the char
+// is not a valid hex digit.
+//-----------------------------------------------------------------------------
+int iHexCharToInt( char cValue )
+{
+ int32_t iValue = cValue;
+ if ( (uint32_t)( iValue - '0' ) < 10 )
+ return iValue - '0';
+
+ iValue |= 0x20;
+ if ( (uint32_t)( iValue - 'a' ) < 6 )
+ return iValue - 'a' + 10;
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: These define the set of characters to filter for components (which
+// need all the escaping we can muster) vs. paths (which don't want
+// / and : escaped so we don't break less compliant URL handling code.
+//-----------------------------------------------------------------------------
+static bool CharNeedsEscape_Component( const char c )
+{
+ return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
+ && c != '-' && c != '_' && c != '.');
+}
+static bool CharNeedsEscape_FullPath( const char c )
+{
+ return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
+ && c != '-' && c != '_' && c != '.' && c != '/' && c != ':' );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Internal implementation of encode, works in the strict RFC manner, or
+// with spaces turned to + like HTML form encoding.
+//-----------------------------------------------------------------------------
+void V_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen,
+ bool bUsePlusForSpace, std::function< bool(const char)> fnNeedsEscape )
+{
+ //AssertMsg( nDestLen > 3*nSourceLen, "Target buffer for V_URLEncode should be 3x source length, plus one for terminating null\n" );
+
+ int iDestPos = 0;
+ for ( int i=0; i < nSourceLen; ++i )
+ {
+ // worst case we need 3 additional chars
+ if( (iDestPos+3) > nDestLen )
+ {
+ pchDest[0] = '\0';
+// AssertMsg( false, "Target buffer too short\n" );
+ return;
+ }
+
+ // We allow only a-z, A-Z, 0-9, period, underscore, and hyphen to pass through unescaped.
+ // These are the characters allowed by both the original RFC 1738 and the latest RFC 3986.
+ // Current specs also allow '~', but that is forbidden under original RFC 1738.
+ if ( fnNeedsEscape( pchSource[i] ) )
+ {
+ if ( bUsePlusForSpace && pchSource[i] == ' ' )
+ {
+ pchDest[iDestPos++] = '+';
+ }
+ else
+ {
+ pchDest[iDestPos++] = '%';
+ uint8_t iValue = pchSource[i];
+ if ( iValue == 0 )
+ {
+ pchDest[iDestPos++] = '0';
+ pchDest[iDestPos++] = '0';
+ }
+ else
+ {
+ char cHexDigit1 = cIntToHexDigit( iValue % 16 );
+ iValue /= 16;
+ char cHexDigit2 = cIntToHexDigit( iValue );
+ pchDest[iDestPos++] = cHexDigit2;
+ pchDest[iDestPos++] = cHexDigit1;
+ }
+ }
+ }
+ else
+ {
+ pchDest[iDestPos++] = pchSource[i];
+ }
+ }
+
+ if( (iDestPos+1) > nDestLen )
+ {
+ pchDest[0] = '\0';
+ //AssertMsg( false, "Target buffer too short to terminate\n" );
+ return;
+ }
+
+ // Null terminate
+ pchDest[iDestPos++] = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Internal implementation of decode, works in the strict RFC manner, or
+// with spaces turned to + like HTML form encoding.
+//
+// Returns the amount of space used in the output buffer.
+//-----------------------------------------------------------------------------
+size_t V_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
+{
+ if ( nDecodeDestLen < nEncodedSourceLen )
+ {
+ //AssertMsg( false, "V_URLDecode needs a dest buffer at least as large as the source" );
+ return 0;
+ }
+
+ int iDestPos = 0;
+ for( int i=0; i < nEncodedSourceLen; ++i )
+ {
+ if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
+ {
+ pchDecodeDest[ iDestPos++ ] = ' ';
+ }
+ else if ( pchEncodedSource[i] == '%' )
+ {
+ // Percent signifies an encoded value, look ahead for the hex code, convert to numeric, and use that
+
+ // First make sure we have 2 more chars
+ if ( i < nEncodedSourceLen - 2 )
+ {
+ char cHexDigit1 = pchEncodedSource[i+1];
+ char cHexDigit2 = pchEncodedSource[i+2];
+
+ // Turn the chars into a hex value, if they are not valid, then we'll
+ // just place the % and the following two chars direct into the string,
+ // even though this really shouldn't happen, who knows what bad clients
+ // may do with encoding.
+ bool bValid = false;
+ int iValue = iHexCharToInt( cHexDigit1 );
+ if ( iValue != -1 )
+ {
+ iValue *= 16;
+ int iValue2 = iHexCharToInt( cHexDigit2 );
+ if ( iValue2 != -1 )
+ {
+ iValue += iValue2;
+ pchDecodeDest[ iDestPos++ ] = (char)iValue;
+ bValid = true;
+ }
+ }
+
+ if ( !bValid )
+ {
+ pchDecodeDest[ iDestPos++ ] = '%';
+ pchDecodeDest[ iDestPos++ ] = cHexDigit1;
+ pchDecodeDest[ iDestPos++ ] = cHexDigit2;
+ }
+ }
+
+ // Skip ahead
+ i += 2;
+ }
+ else
+ {
+ pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
+ }
+ }
+
+ // We may not have extra room to NULL terminate, since this can be used on raw data, but if we do
+ // go ahead and do it as this can avoid bugs.
+ if ( iDestPos < nDecodeDestLen )
+ {
+ pchDecodeDest[iDestPos] = 0;
+ }
+
+ return (size_t)iDestPos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+//-----------------------------------------------------------------------------
+void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true, CharNeedsEscape_Component );
+}
+
+
+void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_Component );
+}
+
+void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
+{
+ return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_FullPath );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
+//-----------------------------------------------------------------------------
+size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
+{
+ return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
+}
+
+size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
+{
+ return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
+}
+
+//-----------------------------------------------------------------------------
+void V_StripExtension( std::string &in )
+{
+ // Find the last dot. If it's followed by a dot or a slash, then it's part of a
+ // directory specifier like ../../somedir/./blah.
+ std::string::size_type test = in.rfind( '.' );
+ if ( test != std::string::npos )
+ {
+ // This handles things like ".\blah" or "c:\my@email.com\abc\def\geh"
+ // Which would otherwise wind up with "" and "c:\my@email", respectively.
+ if ( in.rfind( '\\' ) < test && in.rfind( '/' ) < test )
+ {
+ in.resize( test );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tokenizes a string into a vector of strings
+//-----------------------------------------------------------------------------
+std::vector<std::string> TokenizeString( const std::string & sString, char cToken )
+{
+ std::vector<std::string> vecStrings;
+ std::istringstream stream( sString );
+ std::string s;
+ while ( std::getline( stream, s, cToken ) )
+ {
+ vecStrings.push_back( s );
+ }
+ return vecStrings;
+}
+
+// Mozilla: see README.mozilla for more details
+//-----------------------------------------------------------------------------
+// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
+//-----------------------------------------------------------------------------
+// bool RepairUTF8( const char *pbegin, const char *pend, std::string & sOutputUtf8 )
+// {
+// typedef std::codecvt_utf8<char32_t> facet_type;
+// facet_type myfacet;
+
+// std::mbstate_t mystate = std::mbstate_t();
+
+// sOutputUtf8.clear();
+// sOutputUtf8.reserve( pend - pbegin );
+// bool bSqueakyClean = true;
+
+// const char *pmid = pbegin;
+// while ( pmid != pend )
+// {
+// bool bHasError = false;
+// bool bHasValidData = false;
+
+// char32_t out = 0xdeadbeef, *pout;
+// pbegin = pmid;
+// switch ( myfacet.in( mystate, pbegin, pend, pmid, &out, &out + 1, pout ) )
+// {
+// case facet_type::ok:
+// bHasValidData = true;
+// break;
+
+// case facet_type::noconv:
+// // unexpected! always converting type
+// bSqueakyClean = false;
+// break;
+
+// case facet_type::partial:
+// bHasError = pbegin == pmid;
+// if ( bHasError )
+// {
+// bSqueakyClean = false;
+// }
+// else
+// {
+// bHasValidData = true;
+// }
+// break;
+
+// case facet_type::error:
+// bHasError = true;
+// bSqueakyClean = false;
+// break;
+// }
+
+// if ( bHasValidData )
+// {
+// // could convert back, but no need
+// for ( const char *p = pbegin; p != pmid; ++p )
+// {
+// sOutputUtf8 += *p;
+// }
+// }
+
+// if ( bHasError )
+// {
+// sOutputUtf8 += '?';
+// }
+
+// if ( pmid == pbegin )
+// {
+// pmid++;
+// }
+// }
+
+// return bSqueakyClean;
+// }
+
+// //-----------------------------------------------------------------------------
+// // Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
+// //-----------------------------------------------------------------------------
+// bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 )
+// {
+// return RepairUTF8( sInputUtf8.data(), sInputUtf8.data() + sInputUtf8.size(), sOutputUtf8 );
+// }
diff --git a/gfx/vr/service/openvr/src/strtools_public.h b/gfx/vr/service/openvr/src/strtools_public.h
new file mode 100644
index 0000000000..067bbe1b1b
--- /dev/null
+++ b/gfx/vr/service/openvr/src/strtools_public.h
@@ -0,0 +1,156 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <stdint.h>
+#include <sys/types.h>
+#include <vector>
+
+/** returns true if the string has the prefix */
+bool StringHasPrefix( const std::string & sString, const std::string & sPrefix );
+bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix );
+
+/** returns if the string has the suffix */
+bool StringHasSuffix( const std::string &sString, const std::string &sSuffix );
+bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix );
+
+// Mozilla: see README.mozilla for more details
+#if defined( _WIN32 )
+/** converts a UTF-16 string to a UTF-8 string */
+std::string UTF16to8( const wchar_t * in );
+std::string UTF16to8( const std::wstring & in );
+
+/** converts a UTF-8 string to a UTF-16 string */
+std::wstring UTF8to16(const char * in);
+std::wstring UTF8to16( const std::string & in );
+#define Utf16FromUtf8 UTF8to16
+#endif
+
+#if defined( _WIN32 )
+std::string DefaultACPtoUTF8( const char *pszStr );
+#endif
+
+/** Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere */
+bool RepairUTF8( const char *begin, const char *end, std::string & sOutputUtf8 );
+bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 );
+
+/** safely copy a string into a buffer */
+void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource );
+template< size_t bufferSize >
+void strcpy_safe( char (& buffer) [ bufferSize ], const char *pchSource )
+{
+ strcpy_safe( buffer, bufferSize, pchSource );
+}
+
+
+/** converts a string to upper case */
+std::string StringToUpper( const std::string & sString );
+
+/** converts a string to lower case */
+std::string StringToLower( const std::string & sString );
+
+// we stricmp (from WIN) but it isn't POSIX - OSX/LINUX have strcasecmp so just inline bridge to it
+#if defined( OSX ) || defined( LINUX )
+#include <strings.h>
+inline int stricmp(const char *pStr1, const char *pStr2) { return strcasecmp(pStr1,pStr2); }
+#ifndef _stricmp
+#define _stricmp stricmp
+#endif
+inline int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen ) { return strncasecmp( pStr1,pStr2, unBufferLen ); }
+#ifndef _strnicmp
+#define _strnicmp strnicmp
+#endif
+
+#ifndef _vsnprintf_s
+#define _vsnprintf_s vsnprintf
+#endif
+
+#define _TRUNCATE ((size_t)-1)
+
+#endif
+
+#if defined( OSX )
+// behaviors ensure NULL-termination at least as well as _TRUNCATE does, but
+// wcsncpy_s/strncpy_s can non-NULL-terminate, wcslcpy/strlcpy can not.
+// inline errno_t wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
+// {
+// return wcslcpy(strDest, strSource, numberOfElements);
+// }
+
+// inline errno_t strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
+// {
+// return strlcpy(strDest, strSource, numberOfElements);
+// }
+
+#endif
+
+#if defined( LINUX )
+// this implementation does not return whether or not the destination was
+// truncated, but that is straightforward to fix if anybody actually needs the
+// return code.
+#include "string.h"
+inline void wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
+{
+ wcsncpy(strDest, strSource, numberOfElements);
+ strDest[numberOfElements-1] = '\0';
+}
+
+inline void strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
+{
+ strncpy(strDest, strSource, numberOfElements);
+ strDest[numberOfElements-1] = '\0';
+}
+
+#endif
+
+#if defined( _WIN32 ) && _MSC_VER < 1800
+inline uint64_t strtoull(const char *str, char **endptr, int base) { return _strtoui64( str, endptr, base ); }
+#endif
+
+/* Handles copying a std::string into a buffer as would be provided in an API */
+uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen );
+
+/** Returns a std::string from a uint64_t */
+// Mozilla: see README.mozilla for more details
+//std::string Uint64ToString( uint64_t ulValue );
+
+/** returns a uint64_t from a string */
+uint64_t StringToUint64( const std::string & sValue );
+
+//-----------------------------------------------------------------------------
+// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+//-----------------------------------------------------------------------------
+void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+/** Same as V_URLEncode, but without plus for space. */
+void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+/** Same as V_URLEncodeNoPlusForSpace, but without escaping / and : */
+void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
+// This version of the call isn't a strict RFC implementation, but uses + for space as is
+// the standard in HTML form encoding, despite it not being part of the RFC.
+//
+// Dest buffer should be at least as large as source buffer to guarantee room for decode.
+// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
+//-----------------------------------------------------------------------------
+size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
+
+/** Same as V_URLDecode, but without plus for space. */
+size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
+
+//-----------------------------------------------------------------------------
+// Purpose: strip extension from a path
+//-----------------------------------------------------------------------------
+void V_StripExtension( std::string &in );
+
+
+/** Tokenizes a string into a vector of strings */
+std::vector<std::string> TokenizeString( const std::string & sString, char cToken );
diff --git a/gfx/vr/service/openvr/src/vrpathregistry_public.cpp b/gfx/vr/service/openvr/src/vrpathregistry_public.cpp
new file mode 100644
index 0000000000..f40fc1cda1
--- /dev/null
+++ b/gfx/vr/service/openvr/src/vrpathregistry_public.cpp
@@ -0,0 +1,442 @@
+//========= 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 defaultManager] 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 sOverridePath = GetEnvironmentVariable( "VR_PATHREG_OVERRIDE" );
+ if ( !sOverridePath.empty() )
+ return sOverridePath;
+
+ 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;
+}
+
+// Mozilla: see README.mozilla for more details
+// ---------------------------------------------------------------------------
+// Purpose: Loads the config file from its well known location
+// ---------------------------------------------------------------------------
+bool CVRPathRegistry_Public::BLoadFromFile( std::string *psLoadError )
+{
+ std::string sRegPath = GetVRPathRegistryFilename();
+ if( sRegPath.empty() )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to determine VR Path Registry filename";
+ }
+ return false;
+ }
+
+ std::string sRegistryContents = Path_ReadTextFile( sRegPath );
+ if( sRegistryContents.empty() )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to read VR Path Registry from " + sRegPath;
+ }
+ return false;
+ }
+
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ std::istringstream istream( sRegistryContents );
+ std::string sErrors;
+
+// try
+ {
+ if ( !parseFromStream( builder, istream, &root, &sErrors ) )
+ {
+ if ( psLoadError )
+ {
+ *psLoadError = "Unable to parse " + sRegPath + ": " + sErrors;
+ }
+ 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" );
+ }
+ }
+// catch ( ... )
+// {
+// if ( psLoadError )
+// {
+// *psLoadError = "Unable to parse " + sRegPath + ": exception thrown in JSON library";
+// }
+// return false;
+// }
+
+ return true;
+}
+
+
+// ---------------------------------------------------------------------------
+// Purpose: Saves the config file to its well known location
+// ---------------------------------------------------------------------------
+bool CVRPathRegistry_Public::BSaveToFile() const
+{
+ 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::StreamWriterBuilder builder;
+ std::string sRegistryContents = Json::writeString( builder, 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;
+}
+
+
+// ---------------------------------------------------------------------------
+// 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 )
+{
+ std::string sLoadError;
+ CVRPathRegistry_Public pathReg;
+ bool bLoadedRegistry = pathReg.BLoadFromFile( &sLoadError );
+ int nCountEnvironmentVariables = 0;
+ int nRequestedPaths = 0;
+
+ if( psRuntimePath )
+ {
+ nRequestedPaths++;
+ if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
+ {
+ *psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
+ nCountEnvironmentVariables++;
+ }
+ else if( !pathReg.GetRuntimePath().empty() )
+ {
+ *psRuntimePath = pathReg.GetRuntimePath();
+ }
+ else
+ {
+ *psRuntimePath = "";
+ }
+ }
+
+ if( psConfigPath )
+ {
+ nRequestedPaths++;
+ 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 )
+ {
+ nRequestedPaths++;
+ 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 == nRequestedPaths )
+ {
+ // all three environment variables were set, so we don't need the physical file
+ return true;
+ }
+ else if( !bLoadedRegistry )
+ {
+ VRLog( "%s\n", sLoadError.c_str() );
+ }
+
+ return bLoadedRegistry;
+}
+
diff --git a/gfx/vr/service/openvr/src/vrpathregistry_public.h b/gfx/vr/service/openvr/src/vrpathregistry_public.h
new file mode 100644
index 0000000000..776935a011
--- /dev/null
+++ b/gfx/vr/service/openvr/src/vrpathregistry_public.h
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation ============//
+#pragma once
+
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+static const char *k_pchRuntimeOverrideVar = "VR_OVERRIDE";
+static const char *k_pchConfigOverrideVar = "VR_CONFIG_PATH";
+static const char *k_pchLogOverrideVar = "VR_LOG_PATH";
+
+class CVRPathRegistry_Public
+{
+public:
+ static std::string GetVRPathRegistryFilename();
+ static std::string GetOpenVRConfigPath();
+
+public:
+ CVRPathRegistry_Public();
+
+ /** Returns paths using the path registry and the provided override values. Pass NULL for any paths you don't care about.
+ * Returns false if the path registry could not be read. Valid paths might still be returned based on environment variables. */
+ static bool GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers = NULL );
+
+ bool BLoadFromFile( std::string *psError = nullptr );
+ bool BSaveToFile() const;
+
+ bool ToJsonString( std::string &sJsonString );
+
+ // methods to get the current values
+ std::string GetRuntimePath() const;
+ std::string GetConfigPath() const;
+ std::string GetLogPath() const;
+
+protected:
+ typedef std::vector< std::string > StringVector_t;
+
+ // index 0 is the current setting
+ StringVector_t m_vecRuntimePath;
+ StringVector_t m_vecLogPath;
+ StringVector_t m_vecConfigPath;
+
+ // full list of external drivers
+ StringVector_t m_vecExternalDrivers;
+};