diff options
Diffstat (limited to 'ovr_sdk_win_23.0.0/LibOVRKernel/Src/Util/Util_SystemInfo.cpp')
-rw-r--r-- | ovr_sdk_win_23.0.0/LibOVRKernel/Src/Util/Util_SystemInfo.cpp | 2329 |
1 files changed, 2329 insertions, 0 deletions
diff --git a/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Util/Util_SystemInfo.cpp b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Util/Util_SystemInfo.cpp new file mode 100644 index 0000000..42b3ffc --- /dev/null +++ b/ovr_sdk_win_23.0.0/LibOVRKernel/Src/Util/Util_SystemInfo.cpp @@ -0,0 +1,2329 @@ +/************************************************************************************ + +Filename : Util_SystemInfo.cpp +Content : Various operations to get information about the system +Created : September 26, 2014 +Author : Kevin Jenkins + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +Licensed under the Oculus Master SDK License Version 1.0 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +https://developer.oculus.com/licenses/oculusmastersdk-1.0 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "Util_SystemInfo.h" +#include <codecvt> +#include <locale> +#include "Kernel/OVR_Array.h" +#include "Kernel/OVR_Error.h" +#include "Kernel/OVR_System.h" +#include "Kernel/OVR_Threads.h" +#include "Kernel/OVR_Timer.h" +#include "Logging/Logging_Library.h" + +#if defined(OVR_OS_LINUX) +#include <sys/utsname.h> +#endif + +// Includes used for GetBaseOVRPath() +#ifdef OVR_OS_WIN32 +#include <Psapi.h> +#include <Shlobj.h> +#include <Shlwapi.h> +#include <intrin.h> +#include <pdh.h> +#include <pdhMsg.h> +#include <perflib.h> +#include <wtsapi32.h> +#include "Kernel/OVR_Win32_IncludeWindows.h" + +#pragma comment(lib, "Shlwapi") // PathFileExistsW +#pragma comment(lib, "Wtsapi32.lib") // WTSQuerySessionInformation +#pragma comment(lib, "pdh.lib") // PDH +#elif defined(OVR_OS_MS) // Other Microsoft OSs +// Nothing, thanks. +#else +#include <dirent.h> +#include <sys/stat.h> + +#ifdef OVR_OS_LINUX +#include <pwd.h> +#include <unistd.h> +#elif defined(OVR_OS_MAC) +#include <libproc.h> +#endif + +#endif + +static ovrlog::Channel Logger("Util_SystemInfo"); + +namespace OVR { +namespace Util { + +// From http://blogs.msdn.com/b/oldnewthing/archive/2005/02/01/364563.aspx +#if defined(OVR_OS_WIN64) || defined(OVR_OS_WIN32) + +#pragma comment(lib, "version.lib") + +typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + +bool Is64BitWindows() { +#if defined(_WIN64) + return TRUE; // 64-bit programs run only on Win64 +#elif defined(_WIN32) + // 32-bit programs run on both 32-bit and 64-bit Windows + // so must sniff + BOOL f64 = FALSE; + LPFN_ISWOW64PROCESS fnIsWow64Process; + + fnIsWow64Process = + (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"); + if (NULL != fnIsWow64Process) { + return fnIsWow64Process(GetCurrentProcess(), &f64) && f64; + } + return FALSE; +#else + return FALSE; // Win64 does not support Win16 +#endif +} +#endif + +const char* OSAsString() { +#if defined(OVR_OS_IPHONE) + return "IPhone"; +#elif defined(OVR_OS_DARWIN) + return "Darwin"; +#elif defined(OVR_OS_MAC) + return "Mac"; +#elif defined(OVR_OS_BSD) + return "BSD"; +#elif defined(OVR_OS_WIN64) || defined(OVR_OS_WIN32) + if (Is64BitWindows()) + return "Win64"; + else + return "Win32"; +#elif defined(OVR_OS_ANDROID) + return "Android"; +#elif defined(OVR_OS_LINUX) + return "Linux"; +#elif defined(OVR_OS_BSD) + return "BSD"; +#else + return "Other"; +#endif +} + +uint64_t GetGuidInt() { + uint64_t g = Timer::GetTicksNanos(); + + uint64_t lastTime, thisTime; + int j; + // Sleep a small random time, then use the last 4 bits as a source of randomness + for (j = 0; j < 8; j++) { + lastTime = Timer::GetTicksNanos(); + Thread::MSleep(1); + // Note this does not actually sleep for "only" 1 millisecond + // necessarily. Since we do not call timeBeginPeriod(1) explicitly + // before invoking this function it may be sleeping for 10+ milliseconds. + thisTime = Timer::GetTicksNanos(); + uint64_t diff = thisTime - lastTime; + unsigned int diff4Bits = (unsigned int)(diff & 15); + diff4Bits <<= 32 - 4; + diff4Bits >>= j * 4; + ((char*)&g)[j] ^= diff4Bits; + } + + return g; +} + +String GetGuidString() { + uint64_t guid = GetGuidInt(); + + char buff[64]; +#if defined(OVR_CC_MSVC) + snprintf(buff, sizeof(buff), "%I64u", guid); +#else + snprintf(buff, sizeof(buff), "%llu", (unsigned long long)guid); +#endif + return String(buff); +} + +const char* GetProcessInfo() { +#if defined(OVR_CPU_X86_64) + return "64 bit"; +#elif defined(OVR_CPU_X86) + return "32 bit"; +#else + return "TODO"; +#endif +} + +#ifdef OVR_OS_WIN32 + +String OSVersionAsString() { + return GetSystemFileVersionStringW(L"\\kernel32.dll"); +} +String GetSystemFileVersionStringW(const wchar_t filePath[MAX_PATH]) { + wchar_t strFilePath[MAX_PATH]; // Local variable + UINT sysDirLen = GetSystemDirectoryW(strFilePath, ARRAYSIZE(strFilePath)); + if (sysDirLen != 0) { + OVR_wcscat(strFilePath, MAX_PATH, filePath); + return GetFileVersionStringW(strFilePath); + } else { + return "GetSystemDirectoryW failed"; + } +} + +// See +// http://stackoverflow.com/questions/940707/how-do-i-programatically-get-the-version-of-a-dll-or-exe-file +String GetFileVersionStringW(const wchar_t filePath[MAX_PATH]) { + String result; + + DWORD dwSize = GetFileVersionInfoSizeW(filePath, NULL); + if (dwSize == 0) { + Logger.LogDebugF( + "Error in GetFileVersionInfoSizeW: %d (for %s)", GetLastError(), String(filePath).ToCStr()); + result = String(filePath) + " not found"; + } else { + auto pVersionInfo = std::make_unique<BYTE[]>(dwSize); + if (!pVersionInfo) { + Logger.LogDebugF("Out of memory allocating %d bytes (for %s)", dwSize, filePath); + result = "Out of memory"; + } else { + if (!GetFileVersionInfoW(filePath, 0, dwSize, pVersionInfo.get())) { + Logger.LogDebugF( + "Error in GetFileVersionInfo: %d (for %s)", GetLastError(), String(filePath).ToCStr()); + result = "Cannot get version info"; + } else { + VS_FIXEDFILEINFO* pFileInfo = NULL; + UINT pLenFileInfo = 0; + + static HMODULE library; + static std::once_flag once; + std::call_once(once, [&]() { library = LoadLibraryW(L"version.dll"); }); + + decltype(&::VerQueryValueW) const call = reinterpret_cast<decltype(&::VerQueryValueW)>( + GetProcAddress(library, "VerQueryValueW")); + + if (!call(pVersionInfo.get(), L"\\", (LPVOID*)&pFileInfo, &pLenFileInfo)) { + Logger.LogDebugF( + "Error in VerQueryValueW: %d (for %s)", GetLastError(), String(filePath).ToCStr()); + result = "File has no version info"; + } else { + int major = (pFileInfo->dwFileVersionMS >> 16) & 0xffff; + int minor = (pFileInfo->dwFileVersionMS) & 0xffff; + int hotfix = (pFileInfo->dwFileVersionLS >> 16) & 0xffff; + int other = (pFileInfo->dwFileVersionLS) & 0xffff; + + char str[128]; + snprintf(str, 128, "%d.%d.%d.%d", major, minor, hotfix, other); + + result = str; + } + } + } + } + + return result; +} + +String GetCameraDriverVersion() { + return GetSystemFileVersionStringW(L"\\drivers\\OCUSBVID.sys"); +} + +// From http://stackoverflow.com/questions/9524309/enumdisplaydevices-function-not-working-for-me +void GetGraphicsCardList(Array<String>& gpus) { + gpus.Clear(); + DISPLAY_DEVICEW dd; + dd.cb = sizeof(dd); + + DWORD deviceNum = 0; + while (EnumDisplayDevicesW(NULL, deviceNum, &dd, 0)) { + if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + gpus.PushBack(String(dd.DeviceString)); + deviceNum++; + } +} + +String GetProcessorInfo() { + char brand[0x40] = {}; + int cpui[4] = {-1}; + + __cpuidex(cpui, 0x80000002, 0); + + // unsigned int blocks = cpui[0]; + for (int i = 0; i <= 2; ++i) { + __cpuidex(cpui, 0x80000002 + i, 0); + *reinterpret_cast<int*>(brand + i * 16) = cpui[0]; + *reinterpret_cast<int*>(brand + 4 + i * 16) = cpui[1]; + *reinterpret_cast<int*>(brand + 8 + i * 16) = cpui[2]; + *reinterpret_cast<int*>(brand + 12 + i * 16) = cpui[3]; + } + return String(brand, 0x40); +} + +#else + +#ifdef OVR_OS_MAC +// use objective c source + +// used for driver files +String GetFileVersionString(String /*filePath*/) { + return String(); +} + +String GetSystemFileVersionString(String /*filePath*/) { + return String(); +} + +String GetDisplayDriverVersion() { + return String(); +} + +String GetCameraDriverVersion() { + return String(); +} + +#else + +String GetDisplayDriverVersion() { + char info[256] = {0}; + FILE* file = popen("/usr/bin/glxinfo", "r"); + if (file) { + int status = 0; + while (status == 0) { + status = fscanf(file, "%*[^\n]\n"); // Read up till the end of the current line, leaving the + // file pointer at the beginning of the next line + // (skipping any leading whitespace). + OVR_UNUSED(status); // Prevent GCC compiler warning: "ignoring return value of ‘int + // fscanf(FILE*, const char*, ...)" + + status = fscanf(file, "OpenGL version string: %255[^\n]", info); + } + pclose(file); + if (status == 1) { + return String(info); + } + } + return String("No graphics driver details found."); +} + +String GetCameraDriverVersion() { + struct utsname kver; + if (uname(&kver)) { + return String(); + } + return String(kver.release); +} + +void GetGraphicsCardList(OVR::Array<OVR::String>& gpus) { + gpus.Clear(); + + char info[256] = {0}; + FILE* file = popen("/usr/bin/lspci", "r"); + if (file) { + int status = 0; + while (status >= 0) { + status = fscanf(file, "%*[^\n]\n"); // Read up till the end of the current line, leaving the + // file pointer at the beginning of the next line + // (skipping any leading whitespace). + OVR_UNUSED(status); // Prevent GCC compiler warning: "ignoring return value of ‘int + // fscanf(FILE*, const char*, ...)" + + status = fscanf(file, "%*[^ ] VGA compatible controller: %255[^\n]", info); + if (status == 1) { + gpus.PushBack(String(info)); + } + } + pclose(file); + } + if (gpus.GetSizeI() <= 0) { + gpus.PushBack(String("No video card details found.")); + } +} + +String OSVersionAsString() { + char info[256] = {0}; + FILE* file = fopen("/etc/issue", "r"); + if (file) { + int status = fscanf(file, "%255[^\n\\]", info); + fclose(file); + if (status == 1) { + return String(info); + } + } + return String("No OS version details found."); +} + +String GetProcessorInfo() { + char info[256] = {0}; + FILE* file = fopen("/proc/cpuinfo", "r"); + if (file) { + int status = 0; + while (status == 0) { + status = fscanf(file, "%*[^\n]\n"); // Read up till the end of the current line, leaving the + // file pointer at the beginning of the next line + // (skipping any leading whitespace). + OVR_UNUSED(status); // Prevent GCC compiler warning: "ignoring return value of ‘int + // fscanf(FILE*, const char*, ...)" + + status = fscanf(file, "model name : %255[^\n]", info); + } + fclose(file); + if (status == 1) { + return String(info); + } + } + return String("No processor details found."); +} +#endif // OVR_OS_MAC +#endif // WIN32 + +std::string GetProcessPath(pid_t processId, bool fileNameOnly, bool enableErrorResults) { + std::string result; + + if (processId == OVR_INVALID_PID) { + if (enableErrorResults) { + result = "(invalid PID)"; + } + } + +#if defined(OVR_OS_WIN32) + ScopedProcessHANDLE processHandle( + OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId)); + + if (processHandle.IsValid()) { + result = GetProcessPath(processHandle.Get(), fileNameOnly, enableErrorResults); + } else if (enableErrorResults) { + result = "(OpenProcess failure)"; + } + +#elif defined(OVR_OS_LINUX) + char procPath[1024]; + char linkPath[64]; + snprintf(linkPath, OVR_ARRAY_COUNT(linkPath), "/proc/%d/exe", processId); + + int readResult = readlink(linkPath, procPath, OVR_ARRAY_COUNT(procPath)); + + if ((readResult != -1) || (readResult >= int(OVR_ARRAY_COUNT(procPath)))) { + // If the file was deleted, its name will have "(deleted)" after it, which we may want to deal + // with. + procPath[readResult] = '\0'; + OVR_UNUSED(fileNameOnly); // To do. + } else + procPath[0] = '\0'; + result = procPath; + +#elif defined(OVR_OS_MAC) + char procPath[PROC_PIDPATHINFO_MAXSIZE]; + int ret = proc_pidpath(processId, procPath, sizeof(procPath)); + if (ret <= 0) { + Logger.LogDebugF("Unable to lookup PID: %d -- %s", processId, strerror(errno)); + procPath[0] = '\0'; + OVR_UNUSED(fileNameOnly); // To do. + } + result = procPath; +#endif + + return result; +} + +#if defined(_WIN32) +std::string GetProcessPath(HANDLE processHandle, bool fileNameOnly, bool enableErrorResults) { + std::string result; + WCHAR procPathW[MAX_PATH]; + DWORD processNameLength = OVR_ARRAY_COUNT(procPathW); + + if (QueryFullProcessImageNameW(processHandle, 0, procPathW, &processNameLength) != 0) { + result = OVR::UCSStringToUTF8String(procPathW); + + if (fileNameOnly) { + char fileName[_MAX_FNAME]; + fileName[0] = '\0'; + char fileExtension[_MAX_EXT]; + fileExtension[0] = '\0'; + + if (_splitpath_s( + result.c_str(), + nullptr, + 0, + nullptr, + 0, + fileName, + _MAX_FNAME, + fileExtension, + _MAX_EXT) == 0) { + result = fileName; + result += fileExtension; + } // Else leave it as its full path, though that will in practice rarely or never occur. + } + } else if (enableErrorResults) { + DWORD dwProcessId = GetProcessId(processHandle); + DWORD dwLastError = GetLastError(); + OVR::String strError; + if (dwLastError == 31) // Windows reports "A device attached to the system is not functioning." + strError = "Process has previously exited."; + else + OVR::GetSysErrorCodeString(ovrSysErrorCodeType::OS, dwLastError, false, strError); + + char buffer[256]; + snprintf( + buffer, + sizeof(buffer), + "(QueryFullProcessImageNameW failure for process handle 0x%llx, pid %u. Error code: %u: %s)", + (uint64_t)processHandle, + static_cast<int>(dwProcessId), + static_cast<int>(dwLastError), + strError.ToCStr()); + result = buffer; + } + + return result; +} +#endif + +// Same as GetProcessPath, except scrubs the returned process path string if it's something we have +// deemed +// cannot be reported in our log, usually due to privacy measures we have enacted. +std::string GetLoggableProcessPath(pid_t processId, bool fileNameOnly) { + std::string processPath = GetProcessPath(processId, fileNameOnly, true); + + // The following is currently disabled, as we decided to not redact side-loaded file names from + // the server log, and added a ReadMe.txt to the log folder which has a disclaimer about this. + // + //#ifdef OVR_INTERNAL_CODE + // // For internal builds, we do no scrubbing. + //#else + // // For public builds we scrub the process path if it's side-loaded. + // if (IsProcessSideLoaded(GetProcessPath(processId, false))) + // processPath = "(side-loaded app)"; + //#endif + + return processPath; +} + +#if defined(_WIN32) +// Same as GetProcessPath, except scrubs the returned process path string if it's something we have +// deemed +// cannot be reported in our log, usually due to privacy measures we have enacted. +std::string GetLoggableProcessPath(HANDLE processHandle, bool fileNameOnly) { + std::string processPath = GetProcessPath(processHandle, fileNameOnly, true); + + // The current design, which may change, is that we allow logging of process file paths in public + // builds. + //#ifdef OVR_INTERNAL_CODE + // // For internal builds, we do no scrubbing. + //#else + // // For public builds we scrub the process path if it's side-loaded. + // if (IsProcessSideLoaded(processPath)) + // processPath = "(side-loaded app)"; + //#endif + + return processPath; +} +#endif + +//----------------------------------------------------------------------------- +// Get a path under BaseOVRPath +// This version returns a std::wstring object. +std::wstring GetOVRPathW(const wchar_t* subPath, bool create_dir) { +#if defined(_WIN32) + wchar_t fullPath[MAX_PATH]; + SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, NULL, 0, fullPath); + PathAppendW(fullPath, L"Oculus"); + if (subPath != nullptr) { + OVR_ASSERT(subPath[0] != '\0' && subPath[0] != L'/' && subPath[0] != L'\\'); + PathAppendW(fullPath, subPath); + } + + if (create_dir) { + DWORD attrib = ::GetFileAttributesW(fullPath); + bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY); + if (!exists) { + ::CreateDirectoryW(fullPath, NULL); + } + } + + return std::wstring(fullPath); +#else + std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; + + const char* home = getenv("HOME"); + std::string path = home; + +#if defined(OVR_OS_MAC) + path += "/Library/Preferences/Oculus"; +#else + path += "/.config/Oculus"; +#endif + + if (subPath != nullptr) { + OVR_ASSERT(subPath[0] != '\0' && subPath[0] != L'/' && subPath[0] != L'\\'); + std::string narrow = converter.to_bytes(subPath); + path += "/"; + path += narrow; + } + + // Create the Oculus directory if it doesn't exist + if (create_dir) { + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + } else { + closedir(dir); + } + } + + return converter.from_bytes(path); +#endif +} +//----------------------------------------------------------------------------- +// Get a path under BaseOVRPath +// This version returns an OVR::String. +String GetOVRPath(const wchar_t* subPath, bool create_dir) { + return OVR::UCSStringToOVRString(GetOVRPathW(subPath, create_dir)); +} +//----------------------------------------------------------------------------- +// Get the (wide) path for local app data. +std::wstring GetBaseOVRPathW(bool create_dir) { + return GetOVRPathW(nullptr, create_dir); +} +//----------------------------------------------------------------------------- +// Get the path for local app data. +// This version returns an OVR::String. +String GetBaseOVRPath(bool create_dir) { + return GetOVRPath(nullptr, create_dir); +} + +#ifdef _WIN32 + +// subKey and stringName must point to 256 or greater buffers. +static bool +ParseRegistryPath(const char* path, HKEY& rootKey, wchar_t* subKey, wchar_t* stringName) { + std::wstring pathW = OVR::UTF8StringToUCSString(path); + + // We need to convert the path to a Windows registry path. + // "/HKEY_LOCAL_MACHINE/Software/Oculus/ForceGPUDriverVersionAcceptance" needs to convert + // to HKEY_LOCAL_MACHINE, "Software\\Oculus", "ForceGPUDriverVersionAcceptance". + std::replace(pathW.begin(), pathW.end(), L'/', L'\\'); // Convert / -> \ + + // Find the root key + if (pathW.find(L"\\HKEY_LOCAL_MACHINE\\") == 0) { + pathW.erase(0, wcslen(L"\\HKEY_LOCAL_MACHINE\\")); + rootKey = HKEY_LOCAL_MACHINE; + } else if (pathW.find(L"\\HKEY_CLASSES_ROOT\\") == 0) { + pathW.erase(0, wcslen(L"\\HKEY_CLASSES_ROOT\\")); + rootKey = HKEY_CLASSES_ROOT; + } else if (pathW.find(L"\\HKEY_CURRENT_CONFIG\\") == 0) { + pathW.erase(0, wcslen(L"\\HKEY_CURRENT_CONFIG\\")); + rootKey = HKEY_CURRENT_CONFIG; + } else if (pathW.find(L"\\HKEY_CURRENT_USER\\") == 0) { + pathW.erase(0, wcslen(L"\\HKEY_CURRENT_USER\\")); + rootKey = HKEY_CURRENT_USER; + } else if (pathW.find(L"\\HKEY_USERS\\") == 0) { + pathW.erase(0, wcslen(L"\\HKEY_USERS\\")); + rootKey = HKEY_USERS; + } else { + rootKey = 0; + subKey[0] = L'\0'; + stringName[0] = L'\0'; + return false; + } + + // pathW now looks like: "Software\\Oculus\\ForceGPUDriverVersionAcceptance" + + size_t lastSeparator = pathW.rfind('\\'); + if ((lastSeparator == std::wstring::npos) || ((pathW.length() - lastSeparator) > 256)) + return false; + + OVR_strlcpy(stringName, &pathW[lastSeparator + 1], 256); + pathW.erase(lastSeparator, pathW.length() - lastSeparator); + + // stringName now looks like: "ForceGPUDriverVersionAcceptance" + // pathW now looks like: "Software\\Oculus" + + if ((pathW.length()) >= 256) + return false; + + OVR_strlcpy(subKey, pathW.c_str(), 256); + + return true; +} + +#endif // _WIN32 + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// SettingsManager +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#if (__cplusplus >= 201103L) || (defined(_MSC_VER) && (_MSC_VER >= 1800)) // C++11 required + +SettingsManager DefaultSettingsManager; + +#ifdef _WIN32 +SettingsManager::Win32RegistryAnyValue::Win32RegistryAnyValue() + : type(REG_NONE), binaryData(), dwordData(), qwordData(), stringData(), stringArrayData() {} +#endif + +void SettingsManager::AddAlternativeLocation(const char* path) { +#ifdef _WIN32 + auto it = + std::find(RegistryAlternativeLocations.begin(), RegistryAlternativeLocations.end(), path); + if (it == RegistryAlternativeLocations.end()) + RegistryAlternativeLocations.insert(path); +#else + (void)path; +#endif +} + +void SettingsManager::RemoveAlternativeLocation(const char* path) { +#ifdef _WIN32 + auto it = RegistryAlternativeLocations.find(path); + if (it != RegistryAlternativeLocations.end()) + RegistryAlternativeLocations.erase(it); +#else + (void)path; +#endif +} + +SettingsManager::StringSet SettingsManager::GetAlternativeLocations() const { +#ifdef _WIN32 + return RegistryAlternativeLocations; +#else + return StringSet(); +#endif +} + +void SettingsManager::SetDefaultAlternativeLocations() { +#ifdef _WIN32 + // The following is disabled because we are using this are our default. + // RegistryAlternativeLocations.push_back("/HKEY_LOCAL_MACHINE/SOFTWARE/Oculus/"); + RegistryAlternativeLocations.insert("/HKEY_CURRENT_USER/SOFTWARE/Oculus/"); + RegistryAlternativeLocations.insert("/HKEY_LOCAL_MACHINE/SOFTWARE/Oculus VR, LLC/Oculus/"); + RegistryAlternativeLocations.insert("/HKEY_LOCAL_MACHINE/SOFTWARE/Oculus VR, LLC/LibOVR/"); + RegistryAlternativeLocations.insert( + "/HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/Oculus VR, LLC/Oculus/"); + RegistryAlternativeLocations.insert( + "/HKEY_LOCAL_MACHINE/SOFTWARE/Wow6432Node/Oculus VR, LLC/Oculus/Config/"); + RegistryAlternativeLocations.insert("/HKEY_CURRENT_USER/SOFTWARE/Oculus/Dash/"); + RegistryAlternativeLocations.insert(OVR_XRS_SETTINGS_LOCATION); +#endif +} + +#ifdef _WIN32 + +bool SettingsManager::Win32ReadAnyRegistryValue( + DWORD dwType, + uint8_t* data, + size_t dataSize, + Win32RegistryAnyValue& anyValue) const { + switch (dwType) { + case REG_BINARY: + anyValue.type = dwType; + anyValue.binaryData.assign(data, data + dataSize); + break; + + case REG_DWORD: + anyValue.type = dwType; + anyValue.dwordData = *reinterpret_cast<uint32_t*>(data); + break; + + case REG_QWORD: + anyValue.type = dwType; + anyValue.qwordData = *reinterpret_cast<uint64_t*>(data); + break; + + case REG_SZ: { + case REG_EXPAND_SZ: + anyValue.type = dwType; + std::wstring stringW(reinterpret_cast<wchar_t*>(data), (dataSize / sizeof(wchar_t)) - 1); + anyValue.stringData = + std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(stringW); + break; + } + + case REG_MULTI_SZ: { + anyValue.type = dwType; + + for (wchar_t* p = reinterpret_cast<wchar_t*>(data); *p; p += (wcslen(p) + 1)) { + std::wstring stringW(p); + std::string string8 = + std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(stringW); + anyValue.stringArrayData.emplace_back(string8); + } + break; + } + + default: + // Unsupported type. Leave anyValue.type as REG_NONE. + return false; + } + + return true; +} + +bool SettingsManager::Win32ReadAnyRegistryValue( + const char* path, + Win32RegistryAnyValue& anyValue, + int options) const { + bool success = false; + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + std::string fullPath; // Used in case path is relative. + + anyValue.type = REG_NONE; + + if (path[0] != '/') { // If this is a path relative to the default... make a full path. + fullPath = defaultLocation + path; + path = fullPath.c_str(); + } + + if (!ParseRegistryPath(path, rootKey, subKey, stringName)) + return false; + + int64_t iEnd = + ((options & OptionIgnoreAlternativeLocations) ? 0 + : (int64_t)RegistryAlternativeLocations.size()); + + // We do a loop starting with -1 because we are reading from two locations: the input path and + // an array of alternative locations. -1 is for the input path and 0+ is the alternative + // locations. + for (int64_t i = -1; !success && (i < iEnd); ++i) { + if (i >= 0) { // If the input path didn't find it... + // try reading from the alternative locations. + wchar_t stringNameIgnore[256]; + auto it = RegistryAlternativeLocations.begin(); + std::advance(it, (size_t)i); // This is a little slow, but not commonly called. + ParseRegistryPath(it->c_str(), rootKey, subKey, stringNameIgnore); + } // Else i == -1 and use the input path as-is. + + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + DWORD keyOptions = + KEY_QUERY_VALUE | ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + LONG openResult = RegOpenKeyExW(rootKey, subKey, 0, keyOptions, &hKey); + + if (openResult == ERROR_SUCCESS) { + DWORD dwType; + uint8_t data[2048]; // We don't currently have handling for accepting data larger than this. + DWORD dataSize(2048); // We could add extra logic to support this if needed. + + // RegGetValue is like RegQueryValueEx except that Windows guarantees + // that string data is properly 0-terminated. RegGetValue returns ERROR_FILE_NOT_FOUND if + // the given value doesn't exist. + const DWORD flags = RRF_RT_REG_BINARY | RRF_RT_REG_DWORD | RRF_RT_REG_QWORD | RRF_RT_REG_SZ | + RRF_RT_REG_MULTI_SZ | RRF_RT_REG_EXPAND_SZ | + RRF_NOEXPAND; // NOEXPAND needed for RRF_RT_REG_EXPAND_SZ specifically on Win7 + LONG queryResult = RegGetValueW(hKey, nullptr, stringName, flags, &dwType, data, &dataSize); + + if (queryResult == ERROR_SUCCESS) // We don't currently support handling ERROR_MORE_DATA. + success = Win32ReadAnyRegistryValue(dwType, data, dataSize, anyValue); + + RegCloseKey(hKey); + } + } + + return success; +} + +#endif // _WIN32 + +bool SettingsManager::ReadValue(const char* path, std::vector<uint8_t>& value, int options) { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + + if (!Win32ReadAnyRegistryValue(path, anyValue, options)) + return false; + + switch (anyValue.type) { + case REG_BINARY: + value = anyValue.binaryData; + break; + + case REG_QWORD: + value.assign( + reinterpret_cast<uint8_t*>(&anyValue.qwordData), + reinterpret_cast<uint8_t*>(&anyValue.qwordData) + sizeof(anyValue.qwordData)); + break; + + case REG_DWORD: + value.assign( + reinterpret_cast<uint8_t*>(&anyValue.dwordData), + reinterpret_cast<uint8_t*>(&anyValue.dwordData) + sizeof(anyValue.dwordData)); + break; + + case REG_SZ: + case REG_EXPAND_SZ: + // Copy the trailing '\0'. + value.assign( + anyValue.stringData.c_str(), + anyValue.stringData.c_str() + anyValue.stringData.size() + 1); + break; + + case REG_MULTI_SZ: + if (!anyValue.stringArrayData.empty() && !anyValue.stringArrayData[0].empty()) { + // Copy the trailing '\0'. + value.assign( + anyValue.stringArrayData[0].c_str(), + anyValue.stringArrayData[0].c_str() + anyValue.stringArrayData[0].size() + 1); + } + break; + } + + return true; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::ReadValue(const char* path, uint8_t* data, size_t& dataSize, int options) { + std::vector<uint8_t> value; + + if (ReadValue(path, value, options)) { + if (value.size() <= dataSize) { + dataSize = std::min(value.size(), dataSize); + memcpy(data, value.data(), dataSize); + return true; + } + } + + return false; +} + +bool SettingsManager::ReadValue(const char* path, double& value, int options) { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + + if (!Win32ReadAnyRegistryValue(path, anyValue, options)) + return false; + + try { // Some of the std C++ calls below can throw exceptions. + switch (anyValue.type) { + case REG_BINARY: + // reinterpreting binary as a floating point value is probably not something to rely on. + if (anyValue.binaryData.size() >= 8) + value = *reinterpret_cast<double*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 4) + value = *reinterpret_cast<float*>(anyValue.binaryData.data()); + break; + + case REG_QWORD: + value = (double)anyValue.qwordData; + break; + + case REG_DWORD: + value = (double)anyValue.dwordData; + break; + + case REG_SZ: + case REG_EXPAND_SZ: + value = std::stod(anyValue.stringData); // may throw an execption. + break; + + case REG_MULTI_SZ: + if (!anyValue.stringArrayData.empty() && !anyValue.stringArrayData[0].empty()) + value = std::stod(anyValue.stringArrayData[0]); // may throw an execption. + break; + } + } catch (...) { + // Leave value as-is. + } + + return true; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::ReadValue(const char* path, float& value, int options) { + double valueDouble; + + if (!ReadValue(path, valueDouble, options)) + return false; + + value = (float)valueDouble; + return true; +} + +bool SettingsManager::ReadValue(const char* path, uint64_t& value, int options) { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + + if (!Win32ReadAnyRegistryValue(path, anyValue, options)) + return false; + + try { // Some of the std C++ calls below can throw exceptions. + switch (anyValue.type) { + case REG_BINARY: + if (anyValue.binaryData.size() >= 8) + value = *reinterpret_cast<uint64_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 4) + value = *reinterpret_cast<uint32_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 2) + value = *reinterpret_cast<uint16_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 1) + value = *reinterpret_cast<uint8_t*>(anyValue.binaryData.data()); + break; + + case REG_QWORD: + value = anyValue.qwordData; + break; + + case REG_DWORD: + value = anyValue.dwordData; + break; + + case REG_SZ: + case REG_EXPAND_SZ: + value = std::stoull(anyValue.stringData, 0, 0); // may throw an execption. + break; + + case REG_MULTI_SZ: + if (!anyValue.stringArrayData.empty() && !anyValue.stringArrayData[0].empty()) + value = std::stoull(anyValue.stringArrayData[0], 0, 0); // may throw an execption. + break; + } + } catch (...) { + // Leave value as-is. + } + + return true; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::ReadValue(const char* path, uint32_t& value, int options) { + uint64_t value64; + + if (!ReadValue(path, value64, options)) + return false; + + value = (uint32_t)value64; + return true; +} + +bool SettingsManager::ReadValue(const char* path, int64_t& value, int options) { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + + if (!Win32ReadAnyRegistryValue(path, anyValue, options)) + return false; + + try { // Some of the std C++ calls below can throw exceptions. + switch (anyValue.type) { + case REG_BINARY: + if (anyValue.binaryData.size() >= 8) + value = *reinterpret_cast<int64_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 4) + value = *reinterpret_cast<int32_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 2) + value = *reinterpret_cast<int16_t*>(anyValue.binaryData.data()); + else if (anyValue.binaryData.size() >= 1) + value = *reinterpret_cast<int8_t*>(anyValue.binaryData.data()); + break; + + case REG_QWORD: + value = (int64_t)anyValue.qwordData; + break; + + case REG_DWORD: + value = (int64_t)((int32_t)anyValue.dwordData); + break; + + case REG_SZ: + case REG_EXPAND_SZ: + value = std::stoll(anyValue.stringData, 0, 0); // may throw an execption. + break; + + case REG_MULTI_SZ: + if (!anyValue.stringArrayData.empty() && !anyValue.stringArrayData[0].empty()) + value = std::stoll(anyValue.stringArrayData[0], 0, 0); // may throw an execption. + break; + } + } catch (...) { + // Leave value as-is. + } + + return true; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::ReadValue(const char* path, int32_t& value, int options) { + int64_t value64; + + if (!ReadValue(path, value64, options)) + return false; + + value = (int32_t)value64; + return true; +} + +bool SettingsManager::ReadValue(const char* path, std::string& value, int options) { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + + if (!Win32ReadAnyRegistryValue(path, anyValue, options)) + return false; + + // std::to_string usage below may throw std::bad_alloc, but we don't handle bad_alloc here, + // but rather we catch it at a higher level, as it's an indicator not of a data format problem, + // but of a system problem. In practice std::to_string will virtually never throw. + switch (anyValue.type) { + case REG_BINARY: + // When interpreting binary data as a string, how do we interpret the binary data? + // We write binary under two conditions: if the type is floating point and if the type + // is natively binary. Between floating point and binary, the only one which we can + // Possibly convert to a string with any consistency is floating point. + if (anyValue.binaryData.size() == sizeof(double)) { + const double* d = reinterpret_cast<double*>(anyValue.binaryData.data()); + value = std::to_string(*d); + } else { + // Currently we have no translation for this. We could potentially interpret it as + // string data, but the problem with that is that we would be inconsistent because + // we interpret the binary data as floating point if the length is 8 but a string + // for all other sizes. + return false; + } + break; + + case REG_QWORD: + value = std::to_string(anyValue.qwordData); + break; + + case REG_DWORD: + value = std::to_string(anyValue.dwordData); + break; + + case REG_SZ: + case REG_EXPAND_SZ: + value = anyValue.stringData; + break; + + case REG_MULTI_SZ: + if (!anyValue.stringArrayData.empty() && !anyValue.stringArrayData[0].empty()) + value = anyValue.stringArrayData[0]; + break; + } + + return true; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::ReadValue(const char* path, std::wstring& value, int options) { + std::string value8; + + if (!ReadValue(path, value8, options)) // Read as UTF8 + return false; + + value = std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(value8); + return true; +} + +bool SettingsManager::ReadValue(const char* path, bool& value, int options) { + uint64_t result64; + + if (!ReadValue(path, result64, options)) + return false; + + value = (result64 != 0); + return true; +} + +bool SettingsManager::WriteValue(const char* path, bool value, int options) { +#ifdef _WIN32 + // There's no Windows registry bool type, but the defacto standard for + // supporting it is to use the DWORD type as 0 or 1. + return WriteValue(path, value ? (uint32_t)1 : (uint32_t)0, options); +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, const std::string& str, int options) { + return WriteValue(path, str.data(), str.size(), options); +} + +bool SettingsManager::WriteValue(const char* path, const std::wstring& str, int options) { + return WriteValue(path, str.data(), str.size(), options); +} + +bool SettingsManager::WriteValue( + const char* path, + const wchar_t* value, + size_t valueStrlen, + int options) { + // Caller is required to provide a 0-terminated string. + if (value[valueStrlen] != '\0') { + OVR_FAIL(); + return false; + } + +#ifdef _WIN32 + bool result = false; + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + + if (!ParseRegistryPath(path, rootKey, subKey, stringName)) + return false; + + // RegCreateKeyExW returns success if the key already exists. + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + DWORD keyOptions = KEY_CREATE_SUB_KEY | KEY_SET_VALUE | + ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + LONG regResult = + RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, keyOptions, nullptr, &hKey, nullptr); + // If regResult fails with a value of 5 (ERROR_ACCESS_DENIED), it's often because the process + // is trying to write to HLEY_LOCAL_MACHINE and doesn't have admin privileges. + + if (regResult == ERROR_SUCCESS) { + regResult = RegSetValueExW( + hKey, + stringName, + 0, + REG_SZ, + reinterpret_cast<BYTE*>(const_cast<wchar_t*>(value)), + (DWORD)(((valueStrlen + 1) * sizeof(wchar_t)))); + result = (regResult == ERROR_SUCCESS); + // Note that if the RegSetValueExW failed then we make no attempt to uncreate + // the key that may have been created above via RegCreateKeyExW. + RegCloseKey(hKey); + } + + return result; +#else + OVR_UNUSED2(path, options); + return false; +#endif +} + +bool SettingsManager::WriteValue( + const char* path, + const char* value, + size_t valueStrlen, + int options) { +#ifdef _WIN32 + std::wstring stringW = std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes( + value, value + valueStrlen); + return SettingsManager::WriteValue(path, stringW.c_str(), stringW.size(), options); +#else + OVR_UNUSED4(path, value, valueStrlen, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, uint32_t value, int options) { +#ifdef _WIN32 + bool result = false; + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + std::string fullPath; // Used in case path is relative. + + if (path[0] != '/') { // If this is a path relative to the default... make a full path. + fullPath = defaultLocation + path; + path = fullPath.c_str(); + } + + if (!ParseRegistryPath(path, rootKey, subKey, stringName)) + return false; + + // RegCreateKeyExW returns success if the key already exists. + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + DWORD keyOptions = KEY_CREATE_SUB_KEY | KEY_SET_VALUE | + ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + LONG regResult = + RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, keyOptions, nullptr, &hKey, nullptr); + // If regResult fails with a value of 5 (ERROR_ACCESS_DENIED), it's often because the process + // is trying to write to HLEY_LOCAL_MACHINE and doesn't have admin privileges. + + if (regResult == ERROR_SUCCESS) { + regResult = RegSetValueExW( + hKey, stringName, 0, REG_DWORD, reinterpret_cast<BYTE*>(&value), sizeof(value)); + result = (regResult == ERROR_SUCCESS); + // Note that if the RegSetValueExW failed then we make no attempt to uncreate + // the key that may have been created above via RegCreateKeyExW. + RegCloseKey(hKey); + } + + return result; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, int32_t value, int options) { +#ifdef _WIN32 + // If we write it as uint32_t bits, when we read it back as int32_t bits it will be as expected. + return SettingsManager::WriteValue(path, (uint32_t)value, options); +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, uint64_t value, int options) { +#ifdef _WIN32 + bool result = false; + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + std::string fullPath; // Used in case path is relative. + + if (path[0] != '/') { // If this is a path relative to the default... make a full path. + fullPath = defaultLocation + path; + path = fullPath.c_str(); + } + + if (!ParseRegistryPath(path, rootKey, subKey, stringName)) + return false; + + // RegCreateKeyExW returns success if the key already exists. + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + DWORD keyOptions = KEY_CREATE_SUB_KEY | KEY_SET_VALUE | + ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + LONG regResult = + RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, keyOptions, nullptr, &hKey, nullptr); + // If regResult fails with a value of 5 (ERROR_ACCESS_DENIED), it's often because the process + // is trying to write to HLEY_LOCAL_MACHINE and doesn't have admin privileges. + + if (regResult == ERROR_SUCCESS) { + regResult = RegSetValueExW( + hKey, stringName, 0, REG_QWORD, reinterpret_cast<BYTE*>(&value), sizeof(value)); + result = (regResult == ERROR_SUCCESS); + // Note that if the RegSetValueExW failed then we make no attempt to uncreate + // the key that may have been created above via RegCreateKeyExW. + RegCloseKey(hKey); + } + + return result; +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, int64_t value, int options) { +#ifdef _WIN32 + // If we write it as uint32_t bits, when we read it back as int32_t bits it will be as expected. + return SettingsManager::WriteValue(path, (uint64_t)value, options); +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, double value, int options) { +#ifdef _WIN32 + // There's no explicit Windows registry support for writing floating point data, + // but the only lossless way to do is is to use REG_BINARY. + return WriteValue(path, reinterpret_cast<const uint8_t*>(&value), sizeof(value), options); +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue(const char* path, float value, int options) { +#ifdef _WIN32 + // There's no explicit Windows registry support for writing floating point data, + // but the only lossless way to do is is to use REG_BINARY. + return WriteValue(path, reinterpret_cast<const uint8_t*>(&value), sizeof(value), options); +#else + OVR_UNUSED3(path, value, options); + return false; +#endif +} + +bool SettingsManager::WriteValue( + const char* path, + const uint8_t* value, + size_t valueSize, + int options) { +#ifdef _WIN32 + bool result = false; + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + std::string fullPath; // Used in case path is relative. + + if (path[0] != '/') { // If this is a path relative to the default... make a full path. + fullPath = defaultLocation + path; + path = fullPath.c_str(); + } + + if (!ParseRegistryPath(path, rootKey, subKey, stringName)) + return false; + + // RegCreateKeyExW returns success if the key already exists. + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + DWORD keyOptions = KEY_CREATE_SUB_KEY | KEY_SET_VALUE | + ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + LONG regResult = + RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, keyOptions, nullptr, &hKey, nullptr); + // If regResult fails with a value of 5 (ERROR_ACCESS_DENIED), it's often because the process + // is trying to write to HLEY_LOCAL_MACHINE and doesn't have admin privileges. + + if (regResult == ERROR_SUCCESS) { + regResult = RegSetValueExW( + hKey, stringName, 0, REG_BINARY, const_cast<uint8_t*>(value), (DWORD)valueSize); + result = (regResult == ERROR_SUCCESS); + // Note that if the RegSetValueExW failed then we make no attempt to uncreate + // the key that may have been created above via RegCreateKeyExW. + RegCloseKey(hKey); + } + + return result; +#else + OVR_UNUSED4(path, value, valueSize, options); + return false; +#endif +} + +bool SettingsManager::ValueExists(const char* path, int options) const { +#ifdef _WIN32 + Win32RegistryAnyValue anyValue; + return Win32ReadAnyRegistryValue(path, anyValue, options); +#else + OVR_UNUSED2(path, options); + return false; +#endif +} + +bool SettingsManager::DeleteValue(const char* path, bool* wasPresent, int options) { +#ifdef _WIN32 + bool result = true; // True until proven false below. + + if (wasPresent) + *wasPresent = false; + + std::vector<std::string> registryLocations; + + if ((options & OptionIgnoreAlternativeLocations) == 0) + registryLocations.insert( + registryLocations.begin(), + RegistryAlternativeLocations.begin(), + RegistryAlternativeLocations.end()); + + registryLocations.emplace_back(std::string(path)); + + for (const auto& pathStr : registryLocations) { + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + std::string fullPath; // Used in case path is relative. + + path = pathStr.c_str(); + + if (path[0] != '/') { // If this is a path relative to the default... make a full path. + fullPath = defaultLocation + path; + path = fullPath.c_str(); + } + + Win32RegistryAnyValue anyValue; + anyValue.type = REG_NONE; + + if (ParseRegistryPath(path, rootKey, subKey, stringName)) { + HKEY hKey; + DWORD keyOptions = KEY_QUERY_VALUE | KEY_SET_VALUE | + ((options & OptionUse32BitDatabase) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY); + + if (RegOpenKeyExW(rootKey, subKey, 0, keyOptions, &hKey) == ERROR_SUCCESS) { + // Unfortunately, Windows provides no way to properly tell if a value deletion + // attempt failed due to the value not existing. The error code from RegDeleteValue + // can't tell you this. You can call RegQueryValue before calling RegDeleteValue to + // tell if it pre-existed, but between your call to RegQueryValue and RegDeleteValue + // another thread may have deleted it and give you a false error return from + // RegDeleteValue. Our best response to this is to check the value again and if it's + // mising then we know somebody else deleted it. + DWORD dwType; + + if (RegGetValueW(hKey, nullptr, stringName, RRF_RT_ANY, &dwType, nullptr, nullptr) == + ERROR_SUCCESS) { + if (wasPresent) + *wasPresent = true; + + if (RegDeleteValueW(hKey, stringName) != ERROR_SUCCESS) // If failed... + result = + (RegGetValueW(hKey, nullptr, stringName, RRF_RT_ANY, &dwType, nullptr, nullptr) != + ERROR_SUCCESS); + // If result fails with a value of 5 (ERROR_ACCESS_DENIED), it's often because the process + // is trying to write to HLEY_LOCAL_MACHINE and doesn't have admin privileges. + } + + RegCloseKey(hKey); + } + } + } + + return result; +#else + OVR_UNUSED2(path, options); + if (wasPresent) + *wasPresent = false; + return true; +#endif +} + +void SettingsManager::SetDefaultLocation(const char* parentPath) { + OVR_ASSERT(parentPath && (parentPath[0] == '/' || parentPath[0] == '\0')); + defaultLocation = parentPath; +} + +std::string SettingsManager::GetDefaultLocation() const { + return defaultLocation; +} + +std::string SettingsManager::EnumerateValues(const char* parentPath, int options) const { + std::string result; + +#ifdef _WIN32 + HKEY rootKey; // e.g. HKEY_CURRENT_USER/ + wchar_t subKey[256]; // e.g. Software/Oculus + wchar_t stringName[256]; // e.g. SomeValue + + if (!parentPath) + parentPath = defaultLocation.c_str(); + + int64_t kEnd = + ((options & OptionIgnoreAlternativeLocations) ? 0 + : (int64_t)RegistryAlternativeLocations.size()); + + // We do a loop starting with -1 because we are reading from two locations: the input path and + // an array of alternative locations. -1 is for the input path and 0+ is the alternative + // locations. + for (int64_t k = -1; k < kEnd; ++k) { + if (k >= 0) { + auto it = RegistryAlternativeLocations.begin(); + std::advance(it, (size_t)k); // This is a little slow, but not commonly called. + parentPath = it->c_str(); + } + // Assert that parentPath begins and ends with /. + OVR_ASSERT(parentPath && (parentPath[0] == '/') && (parentPath[strlen(parentPath) - 1] == '/')); + + if (!ParseRegistryPath(parentPath, rootKey, subKey, stringName)) { + return ""; + } + + HKEY hKey = 0; // Always use the 64 bit registry view. All Oculus runtime software is 64 bit. + LONG openResult = RegOpenKeyExW(rootKey, subKey, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey); + + if (openResult == ERROR_SUCCESS) { + LONG regResult = ERROR_SUCCESS; + + for (DWORD j = 0; regResult != ERROR_NO_MORE_ITEMS; ++j) { + DWORD dwType; + DWORD stringNameSize = OVR_ARRAY_COUNT(stringName); + uint8_t data[2048]; + DWORD dataSize(2048); + + regResult = + RegEnumValueW(hKey, j, stringName, &stringNameSize, nullptr, &dwType, data, &dataSize); + + if (regResult == ERROR_SUCCESS) { // There's also ERROR_MORE_DATA. + Win32RegistryAnyValue anyValue; + + if (Win32ReadAnyRegistryValue(dwType, data, dataSize, anyValue)) { + char buffer[256]; + + snprintf(buffer, sizeof(buffer), "%s%ls: ", parentPath, stringName); + result += buffer; + + switch (dwType) { + case REG_BINARY: + result += "binary: "; + + for (size_t i = 0, iEnd = std::min<size_t>(64, anyValue.binaryData.size()); + i < iEnd; + ++i) { + snprintf(buffer, sizeof(buffer), "%02x ", anyValue.binaryData[i]); + result += buffer; + } + + if (anyValue.binaryData.size() > 64) // If we had to truncate it... + result += "..."; + + result += "\n"; + break; + + case REG_QWORD: + snprintf( + buffer, + sizeof(buffer), + "uint64: %llu (%#llx)\n", + anyValue.qwordData, + anyValue.qwordData); + result += buffer; + break; + + case REG_DWORD: + snprintf( + buffer, + sizeof(buffer), + "uint32: %u (%#x)\n", + anyValue.dwordData, + anyValue.dwordData); + result += buffer; + break; + + case REG_SZ: + case REG_EXPAND_SZ: + static_assert(sizeof(buffer) >= 256, "buffer size failure"); + snprintf( + buffer, + sizeof(buffer), + "string: %.250s%s\n", + anyValue.stringData.c_str(), + anyValue.stringData.size() >= 256 ? "..." : ""); + result += buffer; + break; + + case REG_MULTI_SZ: + result += "string array: "; + for (size_t i = 0; i < std::max<size_t>(8, anyValue.stringArrayData.size()); ++i) { + snprintf( + buffer, + sizeof(buffer), + "%s%.250s%s\n", + i > 0 ? " " : "", + anyValue.stringArrayData[i].c_str(), + anyValue.stringData.size() >= 256 ? "..." : ""); + result += buffer; + } + if (anyValue.stringArrayData.size() >= 256) // If we had to truncate it... + result += "...\n"; + break; + + default: { + OVR_FAIL(); + } + } + } + } + } + } + } +#else + OVR_UNUSED2(parentPath, options); + OVR_FAIL(); +#endif + return result; +} + +#endif //#if (__cplusplus >= 201103L) // C++11 required + +#ifdef _WIN32 + +bool GetRegistryDwordW( + const wchar_t* pSubKey, + const wchar_t* stringName, + DWORD& out, + RegistryDB registryDB, + RegistryBase registryBase) { + HKEY root = (registryBase == RegistryBase::currentUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + DWORD dwType = REG_DWORD; + HKEY hKey = 0; + DWORD value_length = sizeof(DWORD); + + if ((RegOpenKeyExW( + root, + pSubKey, + 0, + KEY_QUERY_VALUE | + ((registryDB == RegistryDB::wow64_32) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY), + &hKey) != ERROR_SUCCESS) || + (RegQueryValueExW(hKey, stringName, NULL, &dwType, (LPBYTE)&out, &value_length) != + ERROR_SUCCESS) || + (dwType != REG_DWORD)) { + out = 0; + RegCloseKey(hKey); + return false; + } + RegCloseKey(hKey); + + return true; +} + +// When reading Oculus registry keys, we recognize that the user may have inconsistently +// used a DWORD 1 vs. a string "1", and so we support either when checking booleans. +bool GetRegistryBoolW( + const wchar_t* pSubKey, + const wchar_t* stringName, + bool defaultValue, + RegistryDB registryDB, + RegistryBase registryBase) { + wchar_t out[MAX_PATH]; + if (GetRegistryStringW(pSubKey, stringName, out, registryDB, registryBase)) { + return (_wtoi64(out) != 0); + } + + DWORD dw; + if (GetRegistryDwordW(pSubKey, stringName, dw, registryDB, registryBase)) { + return (dw != 0); + } + + return defaultValue; +} + +bool GetRegistryStringW( + const wchar_t* pSubKey, + const wchar_t* stringName, + wchar_t out[MAX_PATH], + RegistryDB registryDB, + RegistryBase registryBase) { + HKEY root = (registryBase == RegistryBase::currentUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + DWORD dwType = REG_SZ; + HKEY hKey = 0; + wchar_t value[MAX_PATH + 1]; // +1 because RegQueryValueEx doesn't necessarily 0-terminate. + DWORD value_length = MAX_PATH; + + if ((RegOpenKeyExW( + root, + pSubKey, + 0, + KEY_QUERY_VALUE | + ((registryDB == RegistryDB::wow64_32) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY), + &hKey) != ERROR_SUCCESS) || + (RegQueryValueExW(hKey, stringName, NULL, &dwType, (LPBYTE)&value, &value_length) != + ERROR_SUCCESS) || + (dwType != REG_SZ)) { + out[0] = L'\0'; + RegCloseKey(hKey); + return false; + } + RegCloseKey(hKey); + + value[value_length] = L'\0'; + wcscpy_s(out, MAX_PATH, value); + return true; +} + +bool SetRegistryDwordW( + const wchar_t* pSubKey, + const wchar_t* stringName, + DWORD newValue, + RegistryDB registryDB, + RegistryBase registryBase) { + HKEY root = (registryBase == RegistryBase::currentUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hKey = 0; + + if ((RegCreateKeyExW( + root, + pSubKey, + 0, + nullptr, + 0, + KEY_CREATE_SUB_KEY | KEY_SET_VALUE | + ((registryDB == RegistryDB::wow64_32) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY), + nullptr, + &hKey, + nullptr) != ERROR_SUCCESS) || + (RegSetValueExW( + hKey, stringName, 0, REG_DWORD, reinterpret_cast<BYTE*>(&newValue), sizeof(newValue)) != + ERROR_SUCCESS)) { + RegCloseKey(hKey); + return false; + } + RegCloseKey(hKey); + return true; +} + +bool DeleteRegistryValue( + const wchar_t* pSubKey, + const wchar_t* stringName, + RegistryDB registryDB, + RegistryBase registryBase) { + HKEY root = (registryBase == RegistryBase::currentUser) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hKey = 0; + + if (RegOpenKeyExW( + root, + pSubKey, + 0, + KEY_ALL_ACCESS | + ((registryDB == RegistryDB::wow64_32) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY), + &hKey) != ERROR_SUCCESS) { + RegCloseKey(hKey); + return false; + } + bool result = (RegDeleteValueW(hKey, stringName) == ERROR_SUCCESS); + + RegCloseKey(hKey); + return result; +} + +String GetMachineTags() { + // We check both HKLM and HKCU for machine tags and append HKCU tags onto + // HKLM tags. The tag list is comma separated. + std::wstring allTags; + std::wstring tempStr; + + if (DefaultSettingsManager.ReadValue( + "/HKEY_LOCAL_MACHINE/Software/Oculus/MachineTags", + tempStr, + SettingsManager::OptionIgnoreAlternativeLocations)) + allTags = tempStr; + + if (DefaultSettingsManager.ReadValue( + "/HKEY_CURRENT_USER/Software/Oculus/MachineTags", + tempStr, + SettingsManager::OptionIgnoreAlternativeLocations)) { + if (!allTags.empty()) + allTags += L","; + allTags += tempStr; + } + + return OVR::String(OVR::UCSStringToUTF8String(allTags).c_str()); +} + +std::wstring GetOVRRuntimePathW() { + std::wstring result; + + // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Oculus VR, LLC\Oculus\Base + if (DefaultSettingsManager.ReadValue( + "/HKEY_LOCAL_MACHINE/SOFTWARE/Oculus VR, LLC/Oculus/Base", + result, + SettingsManager::OptionUse32BitDatabase)) { + // Default case + } else { + // If our registry value gets corrupted we should attempt to use + // a sensible value. + result = L"C:\\Program Files\\Oculus\\"; + } + + // At this point, runtimePath is usually "C:\Program Files\Oculus\", so append what we need to + // it. + result += L"Support\\oculus-runtime"; + + return result; +} +#endif // _WIN32 + +#if OVR_UTIL_STD_FILESYSTEM_AVAILABLE + +std::filesystem::path GetCurrentModuleDirectoryPath() { + namespace fs = std::filesystem; + +#ifdef _WIN32 + // Get the module handle to the currently executing code. Can't use NULL or GetModuleHandle(NULL), + // as that will return the parent exe and not the current module (dll or exe). + HMODULE hModule = NULL; + BOOL result = GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPWSTR)GetCurrentModuleDirectoryPath, + &hModule); + + if (result) { // Should always succeed. + std::array<wchar_t, MAX_PATH> path{}; + + if (GetModuleFileNameW(hModule, path.data(), (DWORD)path.size())) { // Should always succeed. + auto fsPath = fs::path(path.data()); + return fsPath.parent_path(); + } + } +#endif + + return fs::path(); +} + +#endif // defined(__cpp_lib_filesystem) + +// Returns if the computer is currently locked +bool CheckIsComputerLocked() { +#ifdef OVR_OS_MS + LPWSTR pBuf = nullptr; + DWORD bytesReturned = 0; + + if (::WTSQuerySessionInformationW( + WTS_CURRENT_SERVER_HANDLE, + WTS_CURRENT_SESSION, + WTSSessionInfoEx, + &pBuf, + &bytesReturned)) { + if (pBuf && bytesReturned >= sizeof(WTSINFOEX)) { + WTSINFOEXW* info = (WTSINFOEXW*)pBuf; + + WTSINFOEX_LEVEL1_W* level1 = (WTSINFOEX_LEVEL1_W*)&info->Data; + + bool isLocked = false; + + if (level1->SessionFlags == WTS_SESSIONSTATE_LOCK) { + isLocked = true; + } else if (level1->SessionFlags != WTS_SESSIONSTATE_UNLOCK) // if not unlocked, we expect + // locked + { + Logger.LogErrorF("Unknown Lock State = %d", (int)level1->SessionFlags); + } + + // Note: On Windows 7, the locked and unlocked flags are reversed! + // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019(v=vs.85).aspx + if (IsAtMostWindowsVersion(WindowsVersion::Windows7_SP1)) { + isLocked = !isLocked; + } + + return isLocked; + } else { + Logger.LogErrorF("Wrong return size from WTSQuerySessionInformation %u", bytesReturned); + } + if (pBuf) { + WTSFreeMemory(pBuf); + } + } +#endif // OVR_OS_MS + return false; +} + +#ifdef OVR_OS_MS + +static INIT_ONCE OSVersionInitOnce = INIT_ONCE_STATIC_INIT; +static uint32_t OSVersion; +static uint32_t OSBuildNumber; + +BOOL CALLBACK VersionCheckInitOnceCallback(PINIT_ONCE, PVOID, PVOID*) { + typedef NTSTATUS(WINAPI * pfnRtlGetVersion)(PRTL_OSVERSIONINFOEXW lpVersionInformation); + + NTSTATUS status = STATUS_DLL_NOT_FOUND; + + HMODULE hNTDll = LoadLibraryW(L"ntdll.dll"); + OVR_ASSERT(hNTDll); + + if (hNTDll) { + status = STATUS_ENTRYPOINT_NOT_FOUND; + + pfnRtlGetVersion pRtlGetVersion = (pfnRtlGetVersion)GetProcAddress(hNTDll, "RtlGetVersion"); + OVR_ASSERT(pRtlGetVersion); + + if (pRtlGetVersion) { + RTL_OSVERSIONINFOEXW OSVersionInfoEx; + OSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVersionInfoEx); + status = pRtlGetVersion(&OSVersionInfoEx); + OVR_ASSERT(status == 0); + + if (status == 0) { + OSVersion = OSVersionInfoEx.dwMajorVersion * 100 + OSVersionInfoEx.dwMinorVersion; + OSBuildNumber = OSVersionInfoEx.dwBuildNumber; + } + } + + FreeLibrary(hNTDll); + } + + if (status != 0) { + Logger.LogErrorF( + "[VersionCheckInitOnceCallback] Failed to obtain OS version information. 0x%08x", status); + } + + return (status == 0); +} + +#endif // OVR_OS_MS + +bool IsAtLeastWindowsVersion(WindowsVersion version) { +#ifdef OVR_OS_MS + if (!InitOnceExecuteOnce(&OSVersionInitOnce, VersionCheckInitOnceCallback, nullptr, nullptr)) { + OVR_ASSERT(false); + return false; + } + + switch (version) { + case WindowsVersion::Windows10_TH2: + return (OSVersion > 1000) || (OSVersion == 1000 && OSBuildNumber >= 10586); + + case WindowsVersion::Windows10: + return (OSVersion >= 1000); + + case WindowsVersion::Windows8_1: + return (OSVersion >= 603); + + case WindowsVersion::Windows8: + return (OSVersion >= 602); + + case WindowsVersion::Windows7_SP1: + return (OSVersion >= 601) && (OSBuildNumber >= 7601); + + default: + OVR_ASSERT(false); // Forget to add a case for a new OS? + return false; + } +#else // OVR_OS_MS + OVR_UNUSED(version); + return false; +#endif // OVR_OS_MS +} + +bool IsAtMostWindowsVersion(WindowsVersion version) { +#ifdef OVR_OS_MS + if (!InitOnceExecuteOnce(&OSVersionInitOnce, VersionCheckInitOnceCallback, nullptr, nullptr)) { + OVR_ASSERT(false); + return false; + } + + switch (version) { + case WindowsVersion::Windows10_TH2: + return (OSVersion < 1000) || (OSVersion == 1000 && OSBuildNumber <= 10586); + + case WindowsVersion::Windows10: + return (OSVersion < 1000) || (OSVersion == 1000 && OSBuildNumber == 10000); + + case WindowsVersion::Windows8_1: + return (OSVersion <= 603); + + case WindowsVersion::Windows8: + return (OSVersion <= 602); + + case WindowsVersion::Windows7_SP1: + return (OSVersion < 601) || (OSVersion == 601 && OSBuildNumber <= 7601); + + default: + OVR_ASSERT(false); // Forget to add a case for a new OS? + return false; + } +#else // OVR_OS_MS + OVR_UNUSED(version); + return false; +#endif // OVR_OS_MS +} + +ProcessMemoryInfo GetCurrentProcessMemoryInfo() { + ProcessMemoryInfo pmi = {}; + +#if defined(_WIN32) + PROCESS_MEMORY_COUNTERS_EX pmce = {}; + pmce.cb = sizeof(pmce); + + // This should always succeed for the current process. + // We need PROCESS_QUERY_LIMITED_INFORMATION rights if we want to read the info from a different + // process. + // This call used nearly 1ms on a sampled computer, and so should be called infrequently. + if (::GetProcessMemoryInfo( + GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmce), sizeof(pmce))) { + pmi.UsedMemory = (uint64_t)pmce.WorkingSetSize; // The set of pages in the virtual address space + // of the process that are currently resident in + // physical memory. + } +#else +// To do: Implement this. +#endif + + return pmi; +} + +#if defined(_WIN32) + +//----------------------------------------------------------------------------- +// PdhHelper is a helper class for querying perf counters using PDH. +// It is implemented as a singleton and initialized upon the first use. +// Currently, it is only used by GetSystemMemoryInfo() to obtain +// page fault information. There is one RateCounter object within +// PdhHelper for that. +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa373083(v=vs.85).aspx +// +class PdhHelper : public SystemSingletonBase<PdhHelper> { + OVR_DECLARE_SINGLETON(PdhHelper); + + public: + // RateCounter encapsulates a single counter that represents rate information. + struct RateCounter { + // Requires a query object and counter path. + RateCounter() : Valid(false), HasLast(false), CounterHandle(0) {} + + ~RateCounter() { + if (CounterHandle) + PdhRemoveCounter(CounterHandle); + } + + // Note this call may take some time to complete (180ms from testing) due to + // PdhAddCounterW, so be mindful if adding a bunch of counter objects in synchronous + // or blocking manner. + void Init(PDH_HQUERY hQuery, LPCWSTR counterPath) { + if (!hQuery) + return; + + PDH_STATUS status = PdhAddCounterW(hQuery, counterPath, 0, &CounterHandle); + if (ERROR_SUCCESS != status) + return; + + Valid = true; + } + + // Call this to obtain the latest counter value. + // Note that PdhHelper::Collect() must be called prior to this call. + double Query() { + if (!Valid) + return 0.0; + + PDH_RAW_COUNTER now; + DWORD counterType; + PDH_STATUS status = PdhGetRawCounterValue(CounterHandle, &counterType, &now); + if (ERROR_SUCCESS == status && PDH_CSTATUS_VALID_DATA == now.CStatus) { + // Calculate the rate (since this is a rate counter). + // FirstValue is the instance count. SecondValue is the timestamp. + double result = (HasLast && (now.SecondValue - Last.SecondValue)) + ? double(now.FirstValue - Last.FirstValue) / + (double(now.SecondValue - Last.SecondValue) * Timer::GetPerfFrequencyInverse()) + : 0.0; + + Last = now; + HasLast = true; + + return result; + } + + return 0.0; + } + + bool Valid; // Whether this counter object was initialized properly + bool HasLast; // Whether we have a previous snapshot. Need it to compute rate. + PDH_RAW_COUNTER Last; // Previous counter values + PDH_HCOUNTER CounterHandle; // PDH handle + }; + + public: + virtual void OnThreadDestroy() override { + CleanUp(); + } + + void CleanUp() { + if (PdhQuery) + PdhCloseQuery(PdhQuery); + } + + // Collects raw counter values for all counters in this query. + bool Collect() { + return ERROR_SUCCESS == PdhCollectQueryData(PdhQuery); + } + + public: + // Here we define the counters that we are interested in. + RateCounter CounterPageFault; + + private: + PDH_HQUERY PdhQuery; +}; + +PdhHelper::PdhHelper() { + PDH_STATUS status = PdhOpenQueryW(NULL, 0, &PdhQuery); + if (ERROR_SUCCESS != status) { + // If this fails, PdhQuery will be null, and subsequent query will + // check for this and return default data if we have no query object. + CleanUp(); + } + + // Initialize the counters + CounterPageFault.Init(PdhQuery, L"\\Memory\\Page Reads/sec"); +} + +PdhHelper::~PdhHelper() {} + +void PdhHelper::OnSystemDestroy() { + delete this; +} + +#endif // _WIN32 + +//----------------------------------------------------------------------------- + +SystemMemoryInfo GetSystemMemoryInfo() { + SystemMemoryInfo smi = {}; + +#if defined(_WIN32) + PdhHelper* ph = PdhHelper::GetInstance(); + + if (ph) { + // A single Collect call is required for the query object regardless of how many counters + // we are interested in. + if (ph->Collect()) { + smi.PageFault = ph->CounterPageFault.Query(); + } else { + // Can't get the counter for some reason. Use default. + smi.PageFault = 0.0; + } + + PERFORMANCE_INFORMATION pi{}; + pi.cb = sizeof(pi); + if (GetPerformanceInfo(&pi, sizeof(pi))) + smi.CommittedTotal = pi.CommitTotal; + } +#endif + + return smi; +} + +static void cpuid(int output[4], int functionNumber) { +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + __cpuidex(output, functionNumber, 0); +#elif defined(__GNUC__) || defined(__clang__) + int a, b, c, d; + __asm("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(functionNumber), "c"(0) :); + output[0] = a; + output[1] = b; + output[2] = c; + output[3] = d; +#endif +} + +CPUInstructionSet GetSupportedCPUInstructionSet(bool* popcntSupported, bool* lzcntSupported) { + static CPUInstructionSet cpuIS = CPUInstructionSet::Unknown; + + if (cpuIS != CPUInstructionSet::Unknown) + return cpuIS; + + cpuIS = CPUInstructionSet::Basic; + + int features[4] = {}; + cpuid(features, 0); + if (features[0] == 0) // If there are no features... + return cpuIS; + + // Check for lzcnt + if (lzcntSupported) { + cpuid(features, 0x80000001); + *lzcntSupported = ((features[2] & (1 << 5)) == 0); + } + + // Check for popcnt + if (popcntSupported) { + cpuid(features, 1); + *popcntSupported = ((features[2] & (1 << 23)) == 0); + } + + cpuid(features, 1); + + if ((features[3] & (1 << 0)) == 0 || // If floating point is not supported... + (features[3] & (1 << 23)) == 0 || // If MMX is not supported... + (features[3] & (1 << 15)) == 0 || // If conditional move is not supported... + (features[3] & (1 << 24)) == 0 || // If FXSAVE is not supported... + (features[3] & (1 << 25)) == 0) // If SSE is not supported... + { + return cpuIS; + } + cpuIS = CPUInstructionSet::SEE1; + + if ((features[3] & (1 << 26)) == 0) // If SSE2 is not supported... + return cpuIS; + cpuIS = CPUInstructionSet::SSE2; + + if ((features[2] & (1 << 0)) == 0) // If SSE3 is not supported... + return cpuIS; + cpuIS = CPUInstructionSet::SSE3; + + if ((features[2] & (1 << 9)) == 0) // If SSSE3 is not supported... + return cpuIS; + cpuIS = CPUInstructionSet::SSSE3; + + if ((features[2] & (1 << 19)) == 0) // If SSE4.1 is not supported... + return cpuIS; + cpuIS = CPUInstructionSet::SSE41; + + if ((features[2] & (1 << 20)) == 0) // If SSE42 is not supproted... + return cpuIS; + cpuIS = CPUInstructionSet::SSE42; + +#if defined(_MSC_VER) + if ((features[2] & (1 << 27)) == 0 || // If OSXSAVE is not supported... + (_xgetbv(0) & 6) != 6 || // If AVX is not recognized by the OS... + (features[2] & (1 << 28)) == 0) // If AVX is not supported by the CPU... + { + return cpuIS; + } +#else +// To do: Implement this. +#endif + + cpuIS = CPUInstructionSet::AVX1; + + cpuid(features, 7); + if ((features[1] & (1 << 5)) == 0) // If AVX2 is not supported... + return cpuIS; + + cpuIS = CPUInstructionSet::AVX2; + return cpuIS; +} + +//----------------------------------------------------------------------------- +// ModuleInfoLookup +// + +OVR::Util::ModuleInfoLookup DefaultModuleInfoLookup; + +const OVR::ModuleInfo* ModuleInfoLookup::GetModuleInfoForAddress(uint64_t address) { + if (ModuleInfoArray.empty()) { + ModuleInfoArray.resize(256); + + size_t requiredCount = OVR::SymbolLookup::GetModuleInfoArray( + &ModuleInfoArray[0], ModuleInfoArray.size(), OVR::SymbolLookup::ModuleSortByAddress); + OVR_ASSERT_AND_UNUSED(requiredCount <= ModuleInfoArray.size(), requiredCount); + } + + OVR::ModuleInfo + moduleInfo; // Construct a temporary so std::lower_bound can binary-search find it. + moduleInfo.baseAddress = address; + moduleInfo.size = 0; + + auto mi = std::lower_bound( + ModuleInfoArray.begin(), + ModuleInfoArray.end(), + moduleInfo, + [](const OVR::ModuleInfo& mi1, const OVR::ModuleInfo& mi2) -> bool { + return ((mi1.baseAddress + mi1.size) < (mi2.baseAddress + mi2.size)); + }); + + if (mi != ModuleInfoArray.end()) { + if ((mi->baseAddress <= address) && (address <= (mi->baseAddress + mi->size))) + return &*mi; + } + + return nullptr; +} + +const OVR::ModuleInfo& ModuleInfoLookup::GetModuleInfoForCurrentModule() { + if (CurrentModuleInfo.baseAddress == kMIBaseAddressInvalid) { + // Use GetInstructionAddress because it happens to be a function we forcibly non-inline. + const OVR::ModuleInfo* mi = GetModuleInfoForAddress((uintptr_t)GetInstructionAddress); + + if (mi) + CurrentModuleInfo = *mi; + } + + return CurrentModuleInfo; +} + +bool ModuleInfoLookup::GetAddressIsFromCurrentModule(uint64_t address) { + const OVR::ModuleInfo* mi = GetModuleInfoForAddress(address); + const OVR::ModuleInfo& miCurrent = GetModuleInfoForCurrentModule(); + + return (mi && (mi->baseAddress == miCurrent.baseAddress)); +} + +} // namespace Util + +} // namespace OVR + +#if defined(_WIN32) +OVR_DEFINE_SINGLETON(OVR::Util::PdhHelper); +#endif |