123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845 |
- // Copyright (c) 2017-2022, The Khronos Group Inc.
- // Copyright (c) 2017-2019 Valve Corporation
- // Copyright (c) 2017-2019 LunarG, Inc.
- //
- // SPDX-License-Identifier: Apache-2.0 OR MIT
- //
- // Initial Authors: Mark Young <[email protected]>, Dave Houlton <[email protected]>
- //
- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
- #define _CRT_SECURE_NO_WARNINGS
- #endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
- #include "manifest_file.hpp"
- #ifdef OPENXR_HAVE_COMMON_CONFIG
- #include "common_config.h"
- #endif // OPENXR_HAVE_COMMON_CONFIG
- #include "filesystem_utils.hpp"
- #include "loader_platform.hpp"
- #include "platform_utils.hpp"
- #include "loader_logger.hpp"
- #include <json/json.h>
- #include <openxr/openxr.h>
- #include <algorithm>
- #include <cstring>
- #include <fstream>
- #include <memory>
- #include <sstream>
- #include <stdexcept>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- #ifndef FALLBACK_CONFIG_DIRS
- #define FALLBACK_CONFIG_DIRS "/etc/xdg"
- #endif // !FALLBACK_CONFIG_DIRS
- #ifndef FALLBACK_DATA_DIRS
- #define FALLBACK_DATA_DIRS "/usr/local/share:/usr/share"
- #endif // !FALLBACK_DATA_DIRS
- #ifndef SYSCONFDIR
- #define SYSCONFDIR "/etc"
- #endif // !SYSCONFDIR
- #ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
- #if JSON_USE_EXCEPTIONS
- #error \
- "Loader is configured to not catch exceptions, but jsoncpp was built with exception-throwing enabled, which could violate the C ABI. One of those two things needs to change."
- #endif // JSON_USE_EXCEPTIONS
- #endif // !XRLOADER_DISABLE_EXCEPTION_HANDLING
- #include "runtime_interface.hpp"
- // Utility functions for finding files in the appropriate paths
- static inline bool StringEndsWith(const std::string &value, const std::string &ending) {
- if (ending.size() > value.size()) {
- return false;
- }
- return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
- }
- // If the file found is a manifest file name, add it to the out_files manifest list.
- static void AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
- if (full_file.empty() || !StringEndsWith(full_file, ".json")) {
- return;
- }
- manifest_files.push_back(full_file);
- }
- // Check the current path for any manifest files. If the provided search_path is a directory, look for
- // all included JSON files in that directory. Otherwise, just check the provided search_path which should
- // be a single filename.
- static void CheckAllFilesInThePath(const std::string &search_path, bool is_directory_list,
- std::vector<std::string> &manifest_files) {
- if (FileSysUtilsPathExists(search_path)) {
- std::string absolute_path;
- if (!is_directory_list) {
- // If the file exists, try to add it
- if (FileSysUtilsIsRegularFile(search_path)) {
- FileSysUtilsGetAbsolutePath(search_path, absolute_path);
- AddIfJson(absolute_path, manifest_files);
- }
- } else {
- std::vector<std::string> files;
- if (FileSysUtilsFindFilesInPath(search_path, files)) {
- for (std::string &cur_file : files) {
- std::string relative_path;
- FileSysUtilsCombinePaths(search_path, cur_file, relative_path);
- if (!FileSysUtilsGetAbsolutePath(relative_path, absolute_path)) {
- continue;
- }
- AddIfJson(absolute_path, manifest_files);
- }
- }
- }
- }
- }
- // Add all manifest files in the provided paths to the manifest_files list. If search_path
- // is made up of directory listings (versus direct manifest file names) search each path for
- // any manifest files.
- static void AddFilesInPath(const std::string &search_path, bool is_directory_list, std::vector<std::string> &manifest_files) {
- std::size_t last_found = 0;
- std::size_t found = search_path.find_first_of(PATH_SEPARATOR);
- std::string cur_search;
- // Handle any path listings in the string (separated by the appropriate path separator)
- while (found != std::string::npos) {
- // substr takes a start index and length.
- std::size_t length = found - last_found;
- cur_search = search_path.substr(last_found, length);
- CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
- // This works around issue if multiple path separator follow each other directly.
- last_found = found;
- while (found == last_found) {
- last_found = found + 1;
- found = search_path.find_first_of(PATH_SEPARATOR, last_found);
- }
- }
- // If there's something remaining in the string, copy it over
- if (last_found < search_path.size()) {
- cur_search = search_path.substr(last_found);
- CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
- }
- }
- // Copy all paths listed in the cur_path string into output_path and append the appropriate relative_path onto the end of each.
- static void CopyIncludedPaths(bool is_directory_list, const std::string &cur_path, const std::string &relative_path,
- std::string &output_path) {
- if (!cur_path.empty()) {
- std::size_t last_found = 0;
- std::size_t found = cur_path.find_first_of(PATH_SEPARATOR);
- // Handle any path listings in the string (separated by the appropriate path separator)
- while (found != std::string::npos) {
- std::size_t length = found - last_found;
- output_path += cur_path.substr(last_found, length);
- if (is_directory_list && (cur_path[found - 1] != '\\' && cur_path[found - 1] != '/')) {
- output_path += DIRECTORY_SYMBOL;
- }
- output_path += relative_path;
- output_path += PATH_SEPARATOR;
- last_found = found;
- found = cur_path.find_first_of(PATH_SEPARATOR, found + 1);
- }
- // If there's something remaining in the string, copy it over
- size_t last_char = cur_path.size() - 1;
- if (last_found != last_char) {
- output_path += cur_path.substr(last_found);
- if (is_directory_list && (cur_path[last_char] != '\\' && cur_path[last_char] != '/')) {
- output_path += DIRECTORY_SYMBOL;
- }
- output_path += relative_path;
- output_path += PATH_SEPARATOR;
- }
- }
- }
- // Look for data files in the provided paths, but first check the environment override to determine if we should use that instead.
- static void ReadDataFilesInSearchPaths(const std::string &override_env_var, const std::string &relative_path, bool &override_active,
- std::vector<std::string> &manifest_files) {
- std::string override_path;
- std::string search_path;
- if (!override_env_var.empty()) {
- bool permit_override = true;
- #ifndef XR_OS_WINDOWS
- if (geteuid() != getuid() || getegid() != getgid()) {
- // Don't allow setuid apps to use the env var
- permit_override = false;
- }
- #endif
- if (permit_override) {
- override_path = PlatformUtilsGetSecureEnv(override_env_var.c_str());
- }
- }
- if (!override_path.empty()) {
- CopyIncludedPaths(true, override_path, "", search_path);
- override_active = true;
- } else {
- override_active = false;
- #if !defined(XR_OS_WINDOWS) && !defined(XR_OS_ANDROID)
- const char home_additional[] = ".local/share/";
- // Determine how much space is needed to generate the full search path
- // for the current manifest files.
- std::string xdg_conf_dirs = PlatformUtilsGetSecureEnv("XDG_CONFIG_DIRS");
- std::string xdg_data_dirs = PlatformUtilsGetSecureEnv("XDG_DATA_DIRS");
- std::string xdg_data_home = PlatformUtilsGetSecureEnv("XDG_DATA_HOME");
- std::string home = PlatformUtilsGetSecureEnv("HOME");
- if (xdg_conf_dirs.empty()) {
- CopyIncludedPaths(true, FALLBACK_CONFIG_DIRS, relative_path, search_path);
- } else {
- CopyIncludedPaths(true, xdg_conf_dirs, relative_path, search_path);
- }
- CopyIncludedPaths(true, SYSCONFDIR, relative_path, search_path);
- #if defined(EXTRASYSCONFDIR)
- CopyIncludedPaths(true, EXTRASYSCONFDIR, relative_path, search_path);
- #endif
- if (xdg_data_dirs.empty()) {
- CopyIncludedPaths(true, FALLBACK_DATA_DIRS, relative_path, search_path);
- } else {
- CopyIncludedPaths(true, xdg_data_dirs, relative_path, search_path);
- }
- if (!xdg_data_home.empty()) {
- CopyIncludedPaths(true, xdg_data_home, relative_path, search_path);
- } else if (!home.empty()) {
- std::string relative_home_path = home_additional;
- relative_home_path += relative_path;
- CopyIncludedPaths(true, home, relative_home_path, search_path);
- }
- #else
- (void)relative_path;
- #endif
- }
- // Now, parse the paths and add any manifest files found in them.
- AddFilesInPath(search_path, true, manifest_files);
- }
- #ifdef XR_OS_LINUX
- // Get an XDG environment variable with a $HOME-relative default
- static std::string GetXDGEnvHome(const char *name, const char *fallback_path) {
- std::string result = PlatformUtilsGetSecureEnv(name);
- if (!result.empty()) {
- return result;
- }
- result = PlatformUtilsGetSecureEnv("HOME");
- if (result.empty()) {
- return result;
- }
- result += "/";
- result += fallback_path;
- return result;
- }
- // Get an XDG environment variable with absolute defaults
- static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_paths) {
- std::string result = PlatformUtilsGetSecureEnv(name);
- if (!result.empty()) {
- return result;
- }
- return fallback_paths;
- }
- // Return the first instance of relative_path occurring in an XDG config dir according to standard
- // precedence order.
- static bool FindXDGConfigFile(const std::string &relative_path, std::string &out) {
- out = GetXDGEnvHome("XDG_CONFIG_HOME", ".config");
- if (!out.empty()) {
- out += "/";
- out += relative_path;
- LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in XDG_CONFIG_HOME: " + out);
- if (FileSysUtilsPathExists(out)) {
- return true;
- }
- }
- std::istringstream iss(GetXDGEnvAbsolute("XDG_CONFIG_DIRS", FALLBACK_CONFIG_DIRS));
- std::string path;
- while (std::getline(iss, path, PATH_SEPARATOR)) {
- if (path.empty()) {
- continue;
- }
- out = path;
- out += "/";
- out += relative_path;
- LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in an entry of XDG_CONFIG_DIRS: " + out);
- if (FileSysUtilsPathExists(out)) {
- return true;
- }
- }
- out = SYSCONFDIR;
- out += "/";
- out += relative_path;
- LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in SYSCONFDIR: " + out);
- if (FileSysUtilsPathExists(out)) {
- return true;
- }
- #if defined(EXTRASYSCONFDIR)
- out = EXTRASYSCONFDIR;
- out += "/";
- out += relative_path;
- LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in EXTRASYSCONFDIR: " + out);
- if (FileSysUtilsPathExists(out)) {
- return true;
- }
- #endif
- out.clear();
- return false;
- }
- #endif
- #ifdef XR_OS_WINDOWS
- // Look for runtime data files in the provided paths, but first check the environment override to determine
- // if we should use that instead.
- static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_location,
- const std::string &default_runtime_value_name,
- std::vector<std::string> &manifest_files) {
- HKEY hkey;
- DWORD access_flags;
- wchar_t value_w[1024];
- DWORD value_size_w = sizeof(value_w); // byte size of the buffer.
- // Generate the full registry location for the registry information
- std::string full_registry_location = OPENXR_REGISTRY_LOCATION;
- full_registry_location += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
- full_registry_location += runtime_registry_location;
- const std::wstring full_registry_location_w = utf8_to_wide(full_registry_location);
- const std::wstring default_runtime_value_name_w = utf8_to_wide(default_runtime_value_name);
- // Use 64 bit regkey for 64bit application, and use 32 bit regkey in WOW for 32bit application.
- access_flags = KEY_QUERY_VALUE;
- LONG open_value = RegOpenKeyExW(HKEY_LOCAL_MACHINE, full_registry_location_w.c_str(), 0, access_flags, &hkey);
- if (ERROR_SUCCESS != open_value) {
- LoaderLogger::LogWarningMessage("",
- "ReadRuntimeDataFilesInRegistry - failed to open registry key " + full_registry_location);
- } else if (ERROR_SUCCESS != RegGetValueW(hkey, nullptr, default_runtime_value_name_w.c_str(),
- RRF_RT_REG_SZ | REG_EXPAND_SZ | RRF_ZEROONFAILURE, NULL,
- reinterpret_cast<LPBYTE>(&value_w), &value_size_w)) {
- LoaderLogger::LogWarningMessage(
- "", "ReadRuntimeDataFilesInRegistry - failed to read registry value " + default_runtime_value_name);
- } else {
- AddFilesInPath(wide_to_utf8(value_w), false, manifest_files);
- }
- }
- // Look for layer data files in the provided paths, but first check the environment override to determine
- // if we should use that instead.
- static void ReadLayerDataFilesInRegistry(const std::string ®istry_location, std::vector<std::string> &manifest_files) {
- const std::wstring full_registry_location_w =
- utf8_to_wide(OPENXR_REGISTRY_LOCATION + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + registry_location);
- auto ReadLayerDataFilesInHive = [&](HKEY hive) {
- HKEY hkey;
- LONG open_value = RegOpenKeyExW(hive, full_registry_location_w.c_str(), 0, KEY_QUERY_VALUE, &hkey);
- if (ERROR_SUCCESS != open_value) {
- return false;
- }
- wchar_t name_w[1024]{};
- LONG rtn_value;
- DWORD name_size = 1023;
- DWORD value;
- DWORD value_size = sizeof(value);
- DWORD key_index = 0;
- while (ERROR_SUCCESS ==
- (rtn_value = RegEnumValueW(hkey, key_index++, name_w, &name_size, NULL, NULL, (LPBYTE)&value, &value_size))) {
- if (value_size == sizeof(value) && value == 0) {
- const std::string filename = wide_to_utf8(name_w);
- AddFilesInPath(filename, false, manifest_files);
- }
- // Reset some items for the next loop
- name_size = 1023;
- }
- RegCloseKey(hkey);
- return true;
- };
- // Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
- const bool readFromCurrentUser = !IsHighIntegrityLevel();
- bool found = ReadLayerDataFilesInHive(HKEY_LOCAL_MACHINE);
- if (readFromCurrentUser) {
- found |= ReadLayerDataFilesInHive(HKEY_CURRENT_USER);
- }
- if (!found) {
- std::string warning_message = "ReadLayerDataFilesInRegistry - failed to read registry location ";
- warning_message += registry_location;
- warning_message += (readFromCurrentUser ? " in either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER" : " in HKEY_LOCAL_MACHINE");
- LoaderLogger::LogWarningMessage("", warning_message);
- }
- }
- #endif // XR_OS_WINDOWS
- ManifestFile::ManifestFile(ManifestFileType type, const std::string &filename, const std::string &library_path)
- : _filename(filename), _type(type), _library_path(library_path) {}
- bool ManifestFile::IsValidJson(const Json::Value &root_node, JsonVersion &version) {
- if (root_node["file_format_version"].isNull() || !root_node["file_format_version"].isString()) {
- LoaderLogger::LogErrorMessage("", "ManifestFile::IsValidJson - JSON file missing \"file_format_version\"");
- return false;
- }
- std::string file_format = root_node["file_format_version"].asString();
- const int num_fields = sscanf(file_format.c_str(), "%u.%u.%u", &version.major, &version.minor, &version.patch);
- // Only version 1.0.0 is defined currently. Eventually we may have more version, but
- // some of the versions may only be valid for layers or runtimes specifically.
- if (num_fields != 3 || version.major != 1 || version.minor != 0 || version.patch != 0) {
- std::ostringstream error_ss;
- error_ss << "ManifestFile::IsValidJson - JSON \"file_format_version\" " << version.major << "." << version.minor << "."
- << version.patch << " is not supported";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return false;
- }
- return true;
- }
- static void GetExtensionProperties(const std::vector<ExtensionListing> &extensions, std::vector<XrExtensionProperties> &props) {
- for (const auto &ext : extensions) {
- auto it =
- std::find_if(props.begin(), props.end(), [&](XrExtensionProperties &prop) { return prop.extensionName == ext.name; });
- if (it != props.end()) {
- it->extensionVersion = std::max(it->extensionVersion, ext.extension_version);
- } else {
- XrExtensionProperties prop = {};
- prop.type = XR_TYPE_EXTENSION_PROPERTIES;
- prop.next = nullptr;
- strncpy(prop.extensionName, ext.name.c_str(), XR_MAX_EXTENSION_NAME_SIZE - 1);
- prop.extensionName[XR_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
- prop.extensionVersion = ext.extension_version;
- props.push_back(prop);
- }
- }
- }
- // Return any instance extensions found in the manifest files in the proper form for
- // OpenXR (XrExtensionProperties).
- void ManifestFile::GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props) {
- GetExtensionProperties(_instance_extensions, props);
- }
- const std::string &ManifestFile::GetFunctionName(const std::string &func_name) const {
- if (!_functions_renamed.empty()) {
- auto found = _functions_renamed.find(func_name);
- if (found != _functions_renamed.end()) {
- return found->second;
- }
- }
- return func_name;
- }
- RuntimeManifestFile::RuntimeManifestFile(const std::string &filename, const std::string &library_path)
- : ManifestFile(MANIFEST_TYPE_RUNTIME, filename, library_path) {}
- static void ParseExtension(Json::Value const &ext, std::vector<ExtensionListing> &extensions) {
- Json::Value ext_name = ext["name"];
- Json::Value ext_version = ext["extension_version"];
- // Allow "extension_version" as a String or a UInt to maintain backwards compatibility, even though it should be a String.
- // Internal Issue 1411: https://gitlab.khronos.org/openxr/openxr/-/issues/1411
- // Internal MR !1867: https://gitlab.khronos.org/openxr/openxr/-/merge_requests/1867
- if (ext_name.isString() && (ext_version.isString() || ext_version.isUInt())) {
- ExtensionListing ext_listing = {};
- ext_listing.name = ext_name.asString();
- if (ext_version.isUInt()) {
- ext_listing.extension_version = ext_version.asUInt();
- } else {
- ext_listing.extension_version = atoi(ext_version.asString().c_str());
- }
- extensions.push_back(ext_listing);
- }
- }
- void ManifestFile::ParseCommon(Json::Value const &root_node) {
- const Json::Value &inst_exts = root_node["instance_extensions"];
- if (!inst_exts.isNull() && inst_exts.isArray()) {
- for (const auto &ext : inst_exts) {
- ParseExtension(ext, _instance_extensions);
- }
- }
- const Json::Value &funcs_renamed = root_node["functions"];
- if (!funcs_renamed.isNull() && !funcs_renamed.empty()) {
- for (Json::ValueConstIterator func_it = funcs_renamed.begin(); func_it != funcs_renamed.end(); ++func_it) {
- if (!(*func_it).isString()) {
- LoaderLogger::LogWarningMessage(
- "", "ManifestFile::ParseCommon " + _filename + " \"functions\" section contains non-string values.");
- continue;
- }
- std::string original_name = func_it.key().asString();
- std::string new_name = (*func_it).asString();
- _functions_renamed.emplace(original_name, new_name);
- }
- }
- }
- void RuntimeManifestFile::CreateIfValid(std::string const &filename,
- std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
- std::ifstream json_stream(filename, std::ifstream::in);
- LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename);
- std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
- if (!json_stream.is_open()) {
- error_ss << "failed to open " << filename << ". Does it exist?";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- Json::CharReaderBuilder builder;
- std::string errors;
- Json::Value root_node = Json::nullValue;
- if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
- error_ss << "failed to parse " << filename << ".";
- if (!errors.empty()) {
- error_ss << " (Error message: " << errors << ")";
- }
- error_ss << " Is it a valid runtime manifest file?";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- CreateIfValid(root_node, filename, manifest_files);
- }
- void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,
- std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
- std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
- JsonVersion file_version = {};
- if (!ManifestFile::IsValidJson(root_node, file_version)) {
- error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- const Json::Value &runtime_root_node = root_node["runtime"];
- // The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path". If any of those aren't there,
- // fail.
- if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) {
- error_ss << filename << " is missing required fields. Verify all proper fields exist.";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- std::string lib_path = runtime_root_node["library_path"].asString();
- // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
- // global library path.
- if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) {
- // If the library_path is an absolute path, just use that if it exists
- if (FileSysUtilsIsAbsolutePath(lib_path)) {
- if (!FileSysUtilsPathExists(lib_path)) {
- error_ss << filename << " library " << lib_path << " does not appear to exist";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- } else {
- // Otherwise, treat the library path as a relative path based on the JSON file.
- std::string canonical_path;
- std::string combined_path;
- std::string file_parent;
- // Search relative to the real manifest file, not relative to the symlink
- if (!FileSysUtilsGetCanonicalPath(filename, canonical_path)) {
- // Give relative to the non-canonical path a chance
- canonical_path = filename;
- }
- if (!FileSysUtilsGetParentPath(canonical_path, file_parent) ||
- !FileSysUtilsCombinePaths(file_parent, lib_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
- error_ss << filename << " library " << combined_path << " does not appear to exist";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- lib_path = combined_path;
- }
- }
- // Add this runtime manifest file
- manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));
- // Add any extensions to it after the fact.
- // Handle any renamed functions
- manifest_files.back()->ParseCommon(runtime_root_node);
- }
- // Find all manifest files in the appropriate search paths/registries for the given type.
- XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
- XrResult result = XR_SUCCESS;
- std::string filename = PlatformUtilsGetSecureEnv(OPENXR_RUNTIME_JSON_ENV_VAR);
- if (!filename.empty()) {
- LoaderLogger::LogInfoMessage(
- "", "RuntimeManifestFile::FindManifestFiles - using environment variable override runtime file " + filename);
- } else {
- #ifdef XR_OS_WINDOWS
- std::vector<std::string> filenames;
- ReadRuntimeDataFilesInRegistry("", "ActiveRuntime", filenames);
- if (filenames.size() == 0) {
- LoaderLogger::LogErrorMessage(
- "", "RuntimeManifestFile::FindManifestFiles - failed to find active runtime file in registry");
- return XR_ERROR_RUNTIME_UNAVAILABLE;
- }
- if (filenames.size() > 1) {
- LoaderLogger::LogWarningMessage(
- "", "RuntimeManifestFile::FindManifestFiles - found too many default runtime files in registry");
- }
- filename = filenames[0];
- LoaderLogger::LogInfoMessage("",
- "RuntimeManifestFile::FindManifestFiles - using registry-specified runtime file " + filename);
- #elif defined(XR_OS_LINUX)
- const std::string relative_path =
- "openxr/" + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + "/active_runtime.json";
- if (!FindXDGConfigFile(relative_path, filename)) {
- LoaderLogger::LogErrorMessage(
- "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
- return XR_ERROR_RUNTIME_UNAVAILABLE;
- }
- #else
- #if defined(XR_KHR_LOADER_INIT_SUPPORT)
- Json::Value virtualManifest;
- result = GetPlatformRuntimeVirtualManifest(virtualManifest);
- if (XR_SUCCESS == result) {
- RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
- return result;
- }
- #endif // defined(XR_KHR_LOADER_INIT_SUPPORT)
- if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
- LoaderLogger::LogErrorMessage(
- "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
- return XR_ERROR_RUNTIME_UNAVAILABLE;
- }
- result = XR_SUCCESS;
- LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);
- #endif
- }
- RuntimeManifestFile::CreateIfValid(filename, manifest_files);
- return result;
- }
- ApiLayerManifestFile::ApiLayerManifestFile(ManifestFileType type, const std::string &filename, const std::string &layer_name,
- const std::string &description, const JsonVersion &api_version,
- const uint32_t &implementation_version, const std::string &library_path)
- : ManifestFile(type, filename, library_path),
- _api_version(api_version),
- _layer_name(layer_name),
- _description(description),
- _implementation_version(implementation_version) {}
- void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename,
- std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
- std::ifstream json_stream(filename, std::ifstream::in);
- std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
- if (!json_stream.is_open()) {
- error_ss << "failed to open " << filename << ". Does it exist?";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- Json::CharReaderBuilder builder;
- std::string errors;
- Json::Value root_node = Json::nullValue;
- if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
- error_ss << "failed to parse " << filename << ".";
- if (!errors.empty()) {
- error_ss << " (Error message: " << errors << ")";
- }
- error_ss << " Is it a valid layer manifest file?";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- JsonVersion file_version = {};
- if (!ManifestFile::IsValidJson(root_node, file_version)) {
- error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- Json::Value layer_root_node = root_node["api_layer"];
- // The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
- // If any of those aren't there, fail.
- if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
- layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
- layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
- layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
- error_ss << filename << " is missing required fields. Verify all proper fields exist.";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
- bool enabled = true;
- // Implicit layers require the disable environment variable.
- if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
- error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- // Check if there's an enable environment variable provided
- if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
- std::string env_var = layer_root_node["enable_environment"].asString();
- // If it's not set in the environment, disable the layer
- if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
- enabled = false;
- }
- }
- // Check for the disable environment variable, which must be provided in the JSON
- std::string env_var = layer_root_node["disable_environment"].asString();
- // If the env var is set, disable the layer. Disable env var overrides enable above
- if (PlatformUtilsGetEnvSet(env_var.c_str())) {
- enabled = false;
- }
- // Not enabled, so pretend like it isn't even there.
- if (!enabled) {
- error_ss << "Implicit layer " << filename << " is disabled";
- LoaderLogger::LogInfoMessage("", error_ss.str());
- return;
- }
- }
- std::string layer_name = layer_root_node["name"].asString();
- std::string api_version_string = layer_root_node["api_version"].asString();
- JsonVersion api_version = {};
- const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
- api_version.patch = 0;
- if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
- api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
- error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
- LoaderLogger::LogWarningMessage("", error_ss.str());
- return;
- }
- uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
- std::string library_path = layer_root_node["library_path"].asString();
- // If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
- // global library path.
- if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
- // If the library_path is an absolute path, just use that if it exists
- if (FileSysUtilsIsAbsolutePath(library_path)) {
- if (!FileSysUtilsPathExists(library_path)) {
- error_ss << filename << " library " << library_path << " does not appear to exist";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- } else {
- // Otherwise, treat the library path as a relative path based on the JSON file.
- std::string combined_path;
- std::string file_parent;
- if (!FileSysUtilsGetParentPath(filename, file_parent) ||
- !FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
- error_ss << filename << " library " << combined_path << " does not appear to exist";
- LoaderLogger::LogErrorMessage("", error_ss.str());
- return;
- }
- library_path = combined_path;
- }
- }
- std::string description;
- if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
- description = layer_root_node["description"].asString();
- }
- // Add this layer manifest file
- manifest_files.emplace_back(
- new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
- // Add any extensions to it after the fact.
- manifest_files.back()->ParseCommon(layer_root_node);
- }
- void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &props) const {
- props.layerVersion = _implementation_version;
- props.specVersion = XR_MAKE_VERSION(_api_version.major, _api_version.minor, _api_version.patch);
- strncpy(props.layerName, _layer_name.c_str(), XR_MAX_API_LAYER_NAME_SIZE - 1);
- if (_layer_name.size() >= XR_MAX_API_LAYER_NAME_SIZE - 1) {
- props.layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
- }
- strncpy(props.description, _description.c_str(), XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1);
- if (_description.size() >= XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1) {
- props.description[XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1] = '\0';
- }
- }
- // Find all layer manifest files in the appropriate search paths/registries for the given type.
- XrResult ApiLayerManifestFile::FindManifestFiles(ManifestFileType type,
- std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
- std::string relative_path;
- std::string override_env_var;
- std::string registry_location;
- // Add the appropriate top-level folders for the relative path. These should be
- // the string "openxr/" followed by the API major version as a string.
- relative_path = OPENXR_RELATIVE_PATH;
- relative_path += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
- switch (type) {
- case MANIFEST_TYPE_IMPLICIT_API_LAYER:
- relative_path += OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH;
- override_env_var = "";
- #ifdef XR_OS_WINDOWS
- registry_location = OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION;
- #endif
- break;
- case MANIFEST_TYPE_EXPLICIT_API_LAYER:
- relative_path += OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH;
- override_env_var = OPENXR_API_LAYER_PATH_ENV_VAR;
- #ifdef XR_OS_WINDOWS
- registry_location = OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION;
- #endif
- break;
- default:
- LoaderLogger::LogErrorMessage("", "ApiLayerManifestFile::FindManifestFiles - unknown manifest file requested");
- return XR_ERROR_FILE_ACCESS_ERROR;
- }
- bool override_active = false;
- std::vector<std::string> filenames;
- ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
- #ifdef XR_OS_WINDOWS
- // Read the registry if the override wasn't active.
- if (!override_active) {
- ReadLayerDataFilesInRegistry(registry_location, filenames);
- }
- #endif
- for (std::string &cur_file : filenames) {
- ApiLayerManifestFile::CreateIfValid(type, cur_file, manifest_files);
- }
- return XR_SUCCESS;
- }
|