Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/openxr/src/loader/manifest_file.cpp
9917 views
1
// Copyright (c) 2017-2025 The Khronos Group Inc.
2
// Copyright (c) 2017-2019 Valve Corporation
3
// Copyright (c) 2017-2019 LunarG, Inc.
4
//
5
// SPDX-License-Identifier: Apache-2.0 OR MIT
6
//
7
// Initial Authors: Mark Young <[email protected]>, Dave Houlton <[email protected]>
8
//
9
10
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
11
#define _CRT_SECURE_NO_WARNINGS
12
#endif // defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
13
14
#include "manifest_file.hpp"
15
16
#ifdef OPENXR_HAVE_COMMON_CONFIG
17
#include "common_config.h"
18
#endif // OPENXR_HAVE_COMMON_CONFIG
19
20
#include "filesystem_utils.hpp"
21
#include "loader_init_data.hpp"
22
#include "loader_platform.hpp"
23
#include "platform_utils.hpp"
24
#include "loader_logger.hpp"
25
#include "unique_asset.h"
26
27
#include <json/json.h>
28
#include <openxr/openxr.h>
29
30
#include <algorithm>
31
#include <cstdlib>
32
#include <cstdio>
33
#include <cstring>
34
#include <fstream>
35
#include <memory>
36
#include <sstream>
37
#include <stdexcept>
38
#include <string>
39
#include <unordered_map>
40
#include <utility>
41
#include <vector>
42
43
#ifndef FALLBACK_CONFIG_DIRS
44
#define FALLBACK_CONFIG_DIRS "/etc/xdg"
45
#endif // !FALLBACK_CONFIG_DIRS
46
47
#ifndef FALLBACK_DATA_DIRS
48
#define FALLBACK_DATA_DIRS "/usr/local/share:/usr/share"
49
#endif // !FALLBACK_DATA_DIRS
50
51
#ifndef SYSCONFDIR
52
#define SYSCONFDIR "/etc"
53
#endif // !SYSCONFDIR
54
55
#ifdef XR_USE_PLATFORM_ANDROID
56
#include <android/asset_manager.h>
57
#endif
58
59
#ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING
60
#if JSON_USE_EXCEPTIONS
61
#error \
62
"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."
63
#endif // JSON_USE_EXCEPTIONS
64
#endif // !XRLOADER_DISABLE_EXCEPTION_HANDLING
65
66
#include "runtime_interface.hpp"
67
68
// Utility functions for finding files in the appropriate paths
69
70
static inline bool StringEndsWith(const std::string &value, const std::string &ending) {
71
if (ending.size() > value.size()) {
72
return false;
73
}
74
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
75
}
76
77
// If the file found is a manifest file name, add it to the out_files manifest list.
78
static bool AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
79
if (full_file.empty() || !StringEndsWith(full_file, ".json")) {
80
return false;
81
}
82
manifest_files.push_back(full_file);
83
return true;
84
}
85
86
// Check the current path for any manifest files. If the provided search_path is a directory, look for
87
// all included JSON files in that directory. Otherwise, just check the provided search_path which should
88
// be a single filename.
89
static void CheckAllFilesInThePath(const std::string &search_path, bool is_directory_list,
90
std::vector<std::string> &manifest_files) {
91
if (FileSysUtilsPathExists(search_path)) {
92
std::string absolute_path;
93
if (!is_directory_list) {
94
// If the file exists, try to add it
95
if (FileSysUtilsIsRegularFile(search_path)) {
96
FileSysUtilsGetAbsolutePath(search_path, absolute_path);
97
AddIfJson(absolute_path, manifest_files);
98
}
99
} else {
100
std::vector<std::string> files;
101
if (FileSysUtilsFindFilesInPath(search_path, files)) {
102
for (std::string &cur_file : files) {
103
std::string relative_path;
104
FileSysUtilsCombinePaths(search_path, cur_file, relative_path);
105
if (!FileSysUtilsGetAbsolutePath(relative_path, absolute_path)) {
106
continue;
107
}
108
AddIfJson(absolute_path, manifest_files);
109
}
110
}
111
}
112
}
113
}
114
115
// Add all manifest files in the provided paths to the manifest_files list. If search_path
116
// is made up of directory listings (versus direct manifest file names) search each path for
117
// any manifest files.
118
static void AddFilesInPath(const std::string &search_path, bool is_directory_list, std::vector<std::string> &manifest_files) {
119
std::size_t last_found = 0;
120
std::size_t found = search_path.find_first_of(PATH_SEPARATOR);
121
std::string cur_search;
122
123
// Handle any path listings in the string (separated by the appropriate path separator)
124
while (found != std::string::npos) {
125
// substr takes a start index and length.
126
std::size_t length = found - last_found;
127
cur_search = search_path.substr(last_found, length);
128
129
CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
130
131
// This works around issue if multiple path separator follow each other directly.
132
last_found = found;
133
while (found == last_found) {
134
last_found = found + 1;
135
found = search_path.find_first_of(PATH_SEPARATOR, last_found);
136
}
137
}
138
139
// If there's something remaining in the string, copy it over
140
if (last_found < search_path.size()) {
141
cur_search = search_path.substr(last_found);
142
CheckAllFilesInThePath(cur_search, is_directory_list, manifest_files);
143
}
144
}
145
146
// Copy all paths listed in the cur_path string into output_path and append the appropriate relative_path onto the end of each.
147
static void CopyIncludedPaths(bool is_directory_list, const std::string &cur_path, const std::string &relative_path,
148
std::string &output_path) {
149
if (!cur_path.empty()) {
150
std::size_t last_found = 0;
151
std::size_t found = cur_path.find_first_of(PATH_SEPARATOR);
152
153
// Handle any path listings in the string (separated by the appropriate path separator)
154
while (found != std::string::npos) {
155
std::size_t length = found - last_found;
156
output_path += cur_path.substr(last_found, length);
157
if (is_directory_list && (cur_path[found - 1] != '\\' && cur_path[found - 1] != '/')) {
158
output_path += DIRECTORY_SYMBOL;
159
}
160
output_path += relative_path;
161
output_path += PATH_SEPARATOR;
162
163
last_found = found;
164
found = cur_path.find_first_of(PATH_SEPARATOR, found + 1);
165
}
166
167
// If there's something remaining in the string, copy it over
168
size_t last_char = cur_path.size() - 1;
169
if (last_found != last_char) {
170
output_path += cur_path.substr(last_found);
171
if (is_directory_list && (cur_path[last_char] != '\\' && cur_path[last_char] != '/')) {
172
output_path += DIRECTORY_SYMBOL;
173
}
174
output_path += relative_path;
175
output_path += PATH_SEPARATOR;
176
}
177
}
178
}
179
180
// Look for data files in the provided paths, but first check the environment override to determine if we should use that instead.
181
static void ReadDataFilesInSearchPaths(const std::string &override_env_var, const std::string &relative_path, bool &override_active,
182
std::vector<std::string> &manifest_files) {
183
std::string override_path;
184
std::string search_path;
185
186
if (!override_env_var.empty()) {
187
bool permit_override = true;
188
#ifndef XR_OS_WINDOWS
189
if (geteuid() != getuid() || getegid() != getgid()) {
190
// Don't allow setuid apps to use the env var
191
permit_override = false;
192
}
193
#endif
194
if (permit_override) {
195
override_path = PlatformUtilsGetSecureEnv(override_env_var.c_str());
196
}
197
}
198
199
if (!override_path.empty()) {
200
CopyIncludedPaths(true, override_path, "", search_path);
201
override_active = true;
202
} else {
203
override_active = false;
204
#if !defined(XR_OS_WINDOWS) && !defined(XR_OS_ANDROID)
205
const char home_additional[] = ".local/share/";
206
207
// Determine how much space is needed to generate the full search path
208
// for the current manifest files.
209
std::string xdg_conf_dirs = PlatformUtilsGetSecureEnv("XDG_CONFIG_DIRS");
210
std::string xdg_data_dirs = PlatformUtilsGetSecureEnv("XDG_DATA_DIRS");
211
std::string xdg_data_home = PlatformUtilsGetSecureEnv("XDG_DATA_HOME");
212
std::string home = PlatformUtilsGetSecureEnv("HOME");
213
214
if (xdg_conf_dirs.empty()) {
215
CopyIncludedPaths(true, FALLBACK_CONFIG_DIRS, relative_path, search_path);
216
} else {
217
CopyIncludedPaths(true, xdg_conf_dirs, relative_path, search_path);
218
}
219
220
CopyIncludedPaths(true, SYSCONFDIR, relative_path, search_path);
221
#if defined(EXTRASYSCONFDIR)
222
CopyIncludedPaths(true, EXTRASYSCONFDIR, relative_path, search_path);
223
#endif
224
225
if (xdg_data_dirs.empty()) {
226
CopyIncludedPaths(true, FALLBACK_DATA_DIRS, relative_path, search_path);
227
} else {
228
CopyIncludedPaths(true, xdg_data_dirs, relative_path, search_path);
229
}
230
231
if (!xdg_data_home.empty()) {
232
CopyIncludedPaths(true, xdg_data_home, relative_path, search_path);
233
} else if (!home.empty()) {
234
std::string relative_home_path = home_additional;
235
relative_home_path += relative_path;
236
CopyIncludedPaths(true, home, relative_home_path, search_path);
237
}
238
#elif defined(XR_OS_ANDROID)
239
CopyIncludedPaths(true, "/product/etc", relative_path, search_path);
240
CopyIncludedPaths(true, "/odm/etc", relative_path, search_path);
241
CopyIncludedPaths(true, "/oem/etc", relative_path, search_path);
242
CopyIncludedPaths(true, "/vendor/etc", relative_path, search_path);
243
CopyIncludedPaths(true, "/system/etc", relative_path, search_path);
244
#else
245
(void)relative_path;
246
#endif
247
}
248
249
// Now, parse the paths and add any manifest files found in them.
250
AddFilesInPath(search_path, true, manifest_files);
251
}
252
253
#ifdef XR_OS_LINUX
254
255
// Get an XDG environment variable with a $HOME-relative default
256
static std::string GetXDGEnvHome(const char *name, const char *fallback_path) {
257
std::string result = PlatformUtilsGetSecureEnv(name);
258
if (!result.empty()) {
259
return result;
260
}
261
result = PlatformUtilsGetSecureEnv("HOME");
262
if (result.empty()) {
263
return result;
264
}
265
result += "/";
266
result += fallback_path;
267
return result;
268
}
269
270
// Get an XDG environment variable with absolute defaults
271
static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_paths) {
272
std::string result = PlatformUtilsGetSecureEnv(name);
273
if (!result.empty()) {
274
return result;
275
}
276
return fallback_paths;
277
}
278
279
/// @param rt_dir_prefix Directory prefix with a trailing slash
280
static bool FindEitherActiveRuntimeFilename(const char *prefix_desc, const std::string &rt_dir_prefix, uint16_t major_version,
281
std::string &out) {
282
{
283
std::ostringstream oss;
284
oss << "Looking for active_runtime." XR_ARCH_ABI ".json or active_runtime.json in ";
285
oss << prefix_desc;
286
oss << ": ";
287
oss << rt_dir_prefix;
288
289
LoaderLogger::LogInfoMessage("", oss.str());
290
}
291
{
292
auto decorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime." XR_ARCH_ABI ".json";
293
294
if (FileSysUtilsPathExists(decorated_path)) {
295
out = decorated_path;
296
return true;
297
}
298
}
299
{
300
auto undecorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime.json";
301
302
if (FileSysUtilsPathExists(undecorated_path)) {
303
out = undecorated_path;
304
return true;
305
}
306
}
307
return false;
308
}
309
// Return the first instance of relative_path occurring in an XDG config dir according to standard
310
// precedence order.
311
static bool FindXDGConfigFile(const char *relative_dir, uint16_t major_version, std::string &out) {
312
const std::string message{"Looking for active_runtime." XR_ARCH_ABI ".json or active_runtime.json"};
313
std::string dir_prefix = GetXDGEnvHome("XDG_CONFIG_HOME", ".config");
314
if (!dir_prefix.empty()) {
315
dir_prefix += "/";
316
dir_prefix += relative_dir;
317
if (FindEitherActiveRuntimeFilename("XDG_CONFIG_HOME", dir_prefix, major_version, out)) {
318
return true;
319
}
320
}
321
322
std::istringstream iss(GetXDGEnvAbsolute("XDG_CONFIG_DIRS", FALLBACK_CONFIG_DIRS));
323
std::string path;
324
while (std::getline(iss, path, PATH_SEPARATOR)) {
325
if (path.empty()) {
326
continue;
327
}
328
dir_prefix = std::move(path);
329
dir_prefix += "/";
330
dir_prefix += relative_dir;
331
if (FindEitherActiveRuntimeFilename("an entry of XDG_CONFIG_DIRS", dir_prefix, major_version, out)) {
332
return true;
333
}
334
}
335
336
dir_prefix = SYSCONFDIR;
337
dir_prefix += "/";
338
dir_prefix += relative_dir;
339
if (FindEitherActiveRuntimeFilename("compiled-in SYSCONFDIR", dir_prefix, major_version, out)) {
340
return true;
341
}
342
343
#if defined(EXTRASYSCONFDIR)
344
dir_prefix = EXTRASYSCONFDIR;
345
dir_prefix += "/";
346
dir_prefix += relative_dir;
347
if (FindEitherActiveRuntimeFilename("compiled-in EXTRASYSCONFDIR", dir_prefix, major_version, out)) {
348
return true;
349
}
350
#endif
351
352
out.clear();
353
return false;
354
}
355
356
#endif
357
358
#ifdef XR_OS_WINDOWS
359
360
// Look for runtime data files in the provided paths, but first check the environment override to determine
361
// if we should use that instead.
362
static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_location,
363
const std::string &default_runtime_value_name,
364
std::vector<std::string> &manifest_files) {
365
HKEY hkey;
366
DWORD access_flags;
367
wchar_t value_w[1024];
368
DWORD value_size_w = sizeof(value_w); // byte size of the buffer.
369
370
// Generate the full registry location for the registry information
371
std::string full_registry_location = OPENXR_REGISTRY_LOCATION;
372
full_registry_location += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
373
full_registry_location += runtime_registry_location;
374
375
const std::wstring full_registry_location_w = utf8_to_wide(full_registry_location);
376
const std::wstring default_runtime_value_name_w = utf8_to_wide(default_runtime_value_name);
377
378
// Use 64 bit regkey for 64bit application, and use 32 bit regkey in WOW for 32bit application.
379
access_flags = KEY_QUERY_VALUE;
380
LONG open_value = RegOpenKeyExW(HKEY_LOCAL_MACHINE, full_registry_location_w.c_str(), 0, access_flags, &hkey);
381
382
if (ERROR_SUCCESS != open_value) {
383
LoaderLogger::LogWarningMessage("",
384
"ReadRuntimeDataFilesInRegistry - failed to open registry key " + full_registry_location);
385
return;
386
}
387
388
if (ERROR_SUCCESS != RegGetValueW(hkey, nullptr, default_runtime_value_name_w.c_str(),
389
RRF_RT_REG_SZ | REG_EXPAND_SZ | RRF_ZEROONFAILURE, NULL, reinterpret_cast<LPBYTE>(&value_w),
390
&value_size_w)) {
391
LoaderLogger::LogWarningMessage(
392
"", "ReadRuntimeDataFilesInRegistry - failed to read registry value " + default_runtime_value_name);
393
} else {
394
// Not using AddFilesInPath here (as only api_layer manifest paths allow multiple
395
// separated paths)
396
// Small time-of-check vs time-of-use issue here but it mainly only affects the error message.
397
// It does not introduce a security defect.
398
std::string activeRuntimePath = wide_to_utf8(value_w);
399
if (FileSysUtilsIsRegularFile(activeRuntimePath)) {
400
// If the file exists, try to add it
401
std::string absolute_path;
402
FileSysUtilsGetAbsolutePath(activeRuntimePath, absolute_path);
403
if (!AddIfJson(absolute_path, manifest_files)) {
404
LoaderLogger::LogErrorMessage(
405
"", "ReadRuntimeDataFilesInRegistry - registry runtime path is not json " + activeRuntimePath);
406
}
407
} else {
408
LoaderLogger::LogErrorMessage(
409
"", "ReadRuntimeDataFilesInRegistry - registry runtime path does not exist " + activeRuntimePath);
410
}
411
}
412
413
RegCloseKey(hkey);
414
}
415
416
// Look for layer data files in the provided paths, but first check the environment override to determine
417
// if we should use that instead.
418
static void ReadLayerDataFilesInRegistry(const std::string &registry_location, std::vector<std::string> &manifest_files) {
419
const std::wstring full_registry_location_w =
420
utf8_to_wide(OPENXR_REGISTRY_LOCATION + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + registry_location);
421
422
auto ReadLayerDataFilesInHive = [&](HKEY hive) {
423
HKEY hkey;
424
LONG open_value = RegOpenKeyExW(hive, full_registry_location_w.c_str(), 0, KEY_QUERY_VALUE, &hkey);
425
if (ERROR_SUCCESS != open_value) {
426
return false;
427
}
428
429
wchar_t name_w[1024]{};
430
LONG rtn_value;
431
DWORD name_size = 1023;
432
DWORD value;
433
DWORD value_size = sizeof(value);
434
DWORD key_index = 0;
435
while (ERROR_SUCCESS ==
436
(rtn_value = RegEnumValueW(hkey, key_index++, name_w, &name_size, NULL, NULL, (LPBYTE)&value, &value_size))) {
437
if (value_size == sizeof(value) && value == 0) {
438
const std::string filename = wide_to_utf8(name_w);
439
AddFilesInPath(filename, false, manifest_files);
440
}
441
// Reset some items for the next loop
442
name_size = 1023;
443
}
444
445
RegCloseKey(hkey);
446
447
return true;
448
};
449
450
// Do not allow high integrity processes to act on data that can be controlled by medium integrity processes.
451
const bool readFromCurrentUser = !IsHighIntegrityLevel();
452
453
bool found = ReadLayerDataFilesInHive(HKEY_LOCAL_MACHINE);
454
if (readFromCurrentUser) {
455
found |= ReadLayerDataFilesInHive(HKEY_CURRENT_USER);
456
}
457
458
if (!found) {
459
std::string warning_message = "ReadLayerDataFilesInRegistry - failed to read registry location ";
460
warning_message += registry_location;
461
warning_message += (readFromCurrentUser ? " in either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER" : " in HKEY_LOCAL_MACHINE");
462
LoaderLogger::LogWarningMessage("", warning_message);
463
}
464
}
465
466
#endif // XR_OS_WINDOWS
467
468
ManifestFile::ManifestFile(ManifestFileType type, const std::string &filename, const std::string &library_path)
469
: _filename(filename), _type(type), _library_path(library_path) {}
470
471
bool ManifestFile::IsValidJson(const Json::Value &root_node, JsonVersion &version) {
472
if (root_node["file_format_version"].isNull() || !root_node["file_format_version"].isString()) {
473
LoaderLogger::LogErrorMessage("", "ManifestFile::IsValidJson - JSON file missing \"file_format_version\"");
474
return false;
475
}
476
std::string file_format = root_node["file_format_version"].asString();
477
const int num_fields = sscanf(file_format.c_str(), "%u.%u.%u", &version.major, &version.minor, &version.patch);
478
479
// Only version 1.0.0 is defined currently. Eventually we may have more version, but
480
// some of the versions may only be valid for layers or runtimes specifically.
481
if (num_fields != 3 || version.major != 1 || version.minor != 0 || version.patch != 0) {
482
std::ostringstream error_ss;
483
error_ss << "ManifestFile::IsValidJson - JSON \"file_format_version\" " << version.major << "." << version.minor << "."
484
<< version.patch << " is not supported";
485
LoaderLogger::LogErrorMessage("", error_ss.str());
486
return false;
487
}
488
489
return true;
490
}
491
492
static void GetExtensionProperties(const std::vector<ExtensionListing> &extensions, std::vector<XrExtensionProperties> &props) {
493
for (const auto &ext : extensions) {
494
auto it =
495
std::find_if(props.begin(), props.end(), [&](XrExtensionProperties &prop) { return prop.extensionName == ext.name; });
496
if (it != props.end()) {
497
it->extensionVersion = std::max(it->extensionVersion, ext.extension_version);
498
} else {
499
XrExtensionProperties prop{};
500
prop.type = XR_TYPE_EXTENSION_PROPERTIES;
501
strncpy(prop.extensionName, ext.name.c_str(), XR_MAX_EXTENSION_NAME_SIZE - 1);
502
prop.extensionName[XR_MAX_EXTENSION_NAME_SIZE - 1] = '\0';
503
prop.extensionVersion = ext.extension_version;
504
props.push_back(prop);
505
}
506
}
507
}
508
509
// Return any instance extensions found in the manifest files in the proper form for
510
// OpenXR (XrExtensionProperties).
511
void ManifestFile::GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props) {
512
GetExtensionProperties(_instance_extensions, props);
513
}
514
515
const std::string &ManifestFile::GetFunctionName(const std::string &func_name) const {
516
if (!_functions_renamed.empty()) {
517
auto found = _functions_renamed.find(func_name);
518
if (found != _functions_renamed.end()) {
519
return found->second;
520
}
521
}
522
return func_name;
523
}
524
525
RuntimeManifestFile::RuntimeManifestFile(const std::string &filename, const std::string &library_path)
526
: ManifestFile(MANIFEST_TYPE_RUNTIME, filename, library_path) {}
527
528
static void ParseExtension(Json::Value const &ext, std::vector<ExtensionListing> &extensions) {
529
Json::Value ext_name = ext["name"];
530
Json::Value ext_version = ext["extension_version"];
531
532
// Allow "extension_version" as a String or a UInt to maintain backwards compatibility, even though it should be a String.
533
// Internal Issue 1411: https://gitlab.khronos.org/openxr/openxr/-/issues/1411
534
// Internal MR !1867: https://gitlab.khronos.org/openxr/openxr/-/merge_requests/1867
535
if (ext_name.isString() && (ext_version.isString() || ext_version.isUInt())) {
536
ExtensionListing ext_listing = {};
537
ext_listing.name = ext_name.asString();
538
if (ext_version.isUInt()) {
539
ext_listing.extension_version = ext_version.asUInt();
540
} else {
541
ext_listing.extension_version = atoi(ext_version.asString().c_str());
542
}
543
extensions.push_back(ext_listing);
544
}
545
}
546
547
void ManifestFile::ParseCommon(Json::Value const &root_node) {
548
const Json::Value &inst_exts = root_node["instance_extensions"];
549
if (!inst_exts.isNull() && inst_exts.isArray()) {
550
for (const auto &ext : inst_exts) {
551
ParseExtension(ext, _instance_extensions);
552
}
553
}
554
const Json::Value &funcs_renamed = root_node["functions"];
555
if (!funcs_renamed.isNull() && !funcs_renamed.empty()) {
556
for (Json::ValueConstIterator func_it = funcs_renamed.begin(); func_it != funcs_renamed.end(); ++func_it) {
557
if (!(*func_it).isString()) {
558
LoaderLogger::LogWarningMessage(
559
"", "ManifestFile::ParseCommon " + _filename + " \"functions\" section contains non-string values.");
560
continue;
561
}
562
std::string original_name = func_it.key().asString();
563
std::string new_name = (*func_it).asString();
564
_functions_renamed.emplace(original_name, new_name);
565
}
566
}
567
}
568
569
void RuntimeManifestFile::CreateIfValid(std::string const &filename,
570
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
571
std::ifstream json_stream(filename, std::ifstream::in);
572
573
LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::CreateIfValid - attempting to load " + filename);
574
std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
575
if (!json_stream.is_open()) {
576
error_ss << "failed to open " << filename << ". Does it exist?";
577
LoaderLogger::LogErrorMessage("", error_ss.str());
578
return;
579
}
580
Json::CharReaderBuilder builder;
581
std::string errors;
582
Json::Value root_node = Json::nullValue;
583
if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
584
error_ss << "failed to parse " << filename << ".";
585
if (!errors.empty()) {
586
error_ss << " (Error message: " << errors << ")";
587
}
588
error_ss << " Is it a valid runtime manifest file?";
589
LoaderLogger::LogErrorMessage("", error_ss.str());
590
return;
591
}
592
593
CreateIfValid(root_node, filename, manifest_files);
594
}
595
596
void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,
597
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
598
std::ostringstream error_ss("RuntimeManifestFile::CreateIfValid ");
599
JsonVersion file_version = {};
600
if (!ManifestFile::IsValidJson(root_node, file_version)) {
601
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
602
LoaderLogger::LogErrorMessage("", error_ss.str());
603
return;
604
}
605
const Json::Value &runtime_root_node = root_node["runtime"];
606
// The Runtime manifest file needs the "runtime" root as well as a sub-node for "library_path". If any of those aren't there,
607
// fail.
608
if (runtime_root_node.isNull() || runtime_root_node["library_path"].isNull() || !runtime_root_node["library_path"].isString()) {
609
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
610
LoaderLogger::LogErrorMessage("", error_ss.str());
611
return;
612
}
613
614
std::string lib_path = runtime_root_node["library_path"].asString();
615
616
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
617
// global library path.
618
if (lib_path.find('\\') != std::string::npos || lib_path.find('/') != std::string::npos) {
619
// If the library_path is an absolute path, just use that as-is.
620
if (!FileSysUtilsIsAbsolutePath(lib_path)) {
621
// Otherwise, treat the library path as a relative path based on the JSON file.
622
std::string canonical_path;
623
std::string combined_path;
624
std::string file_parent;
625
// Search relative to the real manifest file, not relative to the symlink
626
if (!FileSysUtilsGetCanonicalPath(filename, canonical_path)) {
627
// Give relative to the non-canonical path a chance
628
canonical_path = filename;
629
}
630
if (!FileSysUtilsGetParentPath(canonical_path, file_parent) ||
631
!FileSysUtilsCombinePaths(file_parent, lib_path, combined_path)) {
632
error_ss << filename << " filesystem operations failed for path " << canonical_path;
633
LoaderLogger::LogErrorMessage("", error_ss.str());
634
return;
635
}
636
lib_path = combined_path;
637
}
638
}
639
640
// Add this runtime manifest file
641
manifest_files.emplace_back(new RuntimeManifestFile(filename, lib_path));
642
643
// Add any extensions to it after the fact.
644
// Handle any renamed functions
645
manifest_files.back()->ParseCommon(runtime_root_node);
646
}
647
648
// Find all manifest files in the appropriate search paths/registries for the given type.
649
XrResult RuntimeManifestFile::FindManifestFiles(const std::string &openxr_command,
650
std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {
651
XrResult result = XR_SUCCESS;
652
std::string filename = PlatformUtilsGetSecureEnv(OPENXR_RUNTIME_JSON_ENV_VAR);
653
if (!filename.empty()) {
654
LoaderLogger::LogInfoMessage(
655
openxr_command,
656
"RuntimeManifestFile::FindManifestFiles - using environment variable override runtime file " + filename);
657
} else {
658
#ifdef XR_OS_WINDOWS
659
std::vector<std::string> filenames;
660
ReadRuntimeDataFilesInRegistry("", "ActiveRuntime", filenames);
661
if (filenames.size() == 0) {
662
LoaderLogger::LogErrorMessage(
663
openxr_command, "RuntimeManifestFile::FindManifestFiles - failed to find active runtime file in registry");
664
return XR_ERROR_RUNTIME_UNAVAILABLE;
665
}
666
if (filenames.size() > 1) {
667
LoaderLogger::LogWarningMessage(
668
openxr_command, "RuntimeManifestFile::FindManifestFiles - found too many default runtime files in registry");
669
}
670
filename = filenames[0];
671
LoaderLogger::LogInfoMessage(openxr_command,
672
"RuntimeManifestFile::FindManifestFiles - using registry-specified runtime file " + filename);
673
#elif defined(XR_OS_LINUX)
674
675
if (!FindXDGConfigFile("openxr/", XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
676
LoaderLogger::LogErrorMessage(
677
openxr_command,
678
"RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
679
return XR_ERROR_RUNTIME_UNAVAILABLE;
680
}
681
#else // !defined(XR_OS_WINDOWS) && !defined(XR_OS_LINUX)
682
683
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
684
Json::Value virtualManifest;
685
result = GetPlatformRuntimeVirtualManifest(virtualManifest);
686
if (XR_SUCCESS == result) {
687
RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files);
688
return result;
689
}
690
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
691
if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) {
692
LoaderLogger::LogErrorMessage(
693
openxr_command,
694
"RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment");
695
return XR_ERROR_RUNTIME_UNAVAILABLE;
696
}
697
result = XR_SUCCESS;
698
LoaderLogger::LogInfoMessage(openxr_command,
699
"RuntimeManifestFile::FindManifestFiles - using global runtime file " + filename);
700
#endif // !defined(XR_OS_WINDOWS) && !defined(XR_OS_LINUX)
701
}
702
RuntimeManifestFile::CreateIfValid(filename, manifest_files);
703
704
return result;
705
}
706
707
ApiLayerManifestFile::ApiLayerManifestFile(ManifestFileType type, const std::string &filename, const std::string &layer_name,
708
const std::string &description, const JsonVersion &api_version,
709
const uint32_t &implementation_version, const std::string &library_path)
710
: ManifestFile(type, filename, library_path),
711
_api_version(api_version),
712
_layer_name(layer_name),
713
_description(description),
714
_implementation_version(implementation_version) {}
715
716
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
717
void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_command, ManifestFileType type,
718
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
719
if (!LoaderInitData::instance().initialized()) {
720
// This will happen for applications that do not call xrInitializeLoaderKHR
721
LoaderLogger::LogWarningMessage(
722
openxr_command,
723
"ApiLayerManifestFile::AddManifestFilesAndroid unable to add manifest files LoaderInitData not initialized.");
724
return;
725
}
726
727
AAssetManager *assetManager = (AAssetManager *)Android_Get_Asset_Manager();
728
std::vector<std::string> filenames;
729
{
730
std::string search_path = "";
731
switch (type) {
732
case MANIFEST_TYPE_IMPLICIT_API_LAYER:
733
search_path = "openxr/1/api_layers/implicit.d/";
734
break;
735
case MANIFEST_TYPE_EXPLICIT_API_LAYER:
736
search_path = "openxr/1/api_layers/explicit.d/";
737
break;
738
default:
739
return;
740
}
741
742
UniqueAssetDir dir{AAssetManager_openDir(assetManager, search_path.c_str())};
743
if (!dir) {
744
return;
745
}
746
const std::string json = ".json";
747
const char *fn = nullptr;
748
while ((fn = AAssetDir_getNextFileName(dir.get())) != nullptr) {
749
const std::string filename = search_path + fn;
750
if (filename.size() < json.size()) {
751
continue;
752
}
753
if (filename.compare(filename.size() - json.size(), json.size(), json) == 0) {
754
filenames.push_back(filename);
755
}
756
}
757
}
758
for (const auto &filename : filenames) {
759
UniqueAsset asset{AAssetManager_open(assetManager, filename.c_str(), AASSET_MODE_BUFFER)};
760
if (!asset) {
761
LoaderLogger::LogWarningMessage(
762
openxr_command, "ApiLayerManifestFile::AddManifestFilesAndroid unable to open asset " + filename + ", skipping");
763
764
continue;
765
}
766
size_t length = AAsset_getLength(asset.get());
767
const char *buf = reinterpret_cast<const char *>(AAsset_getBuffer(asset.get()));
768
if (!buf) {
769
LoaderLogger::LogWarningMessage(
770
openxr_command, "ApiLayerManifestFile::AddManifestFilesAndroid unable to access asset" + filename + ", skipping");
771
772
continue;
773
}
774
std::istringstream json_stream(std::string{buf, length});
775
776
CreateIfValid(type, filename, json_stream, &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files);
777
}
778
}
779
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
780
781
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
782
LibraryLocator locate_library,
783
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
784
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
785
Json::CharReaderBuilder builder;
786
std::string errors;
787
Json::Value root_node = Json::nullValue;
788
if (!Json::parseFromStream(builder, json_stream, &root_node, &errors) || !root_node.isObject()) {
789
error_ss << "failed to parse " << filename << ".";
790
if (!errors.empty()) {
791
error_ss << " (Error message: " << errors << ")";
792
}
793
error_ss << " Is it a valid layer manifest file?";
794
LoaderLogger::LogErrorMessage("", error_ss.str());
795
return;
796
}
797
JsonVersion file_version = {};
798
if (!ManifestFile::IsValidJson(root_node, file_version)) {
799
error_ss << "isValidJson indicates " << filename << " is not a valid manifest file.";
800
LoaderLogger::LogErrorMessage("", error_ss.str());
801
return;
802
}
803
804
Json::Value layer_root_node = root_node["api_layer"];
805
806
// The API Layer manifest file needs the "api_layer" root as well as other sub-nodes.
807
// If any of those aren't there, fail.
808
if (layer_root_node.isNull() || layer_root_node["name"].isNull() || !layer_root_node["name"].isString() ||
809
layer_root_node["api_version"].isNull() || !layer_root_node["api_version"].isString() ||
810
layer_root_node["library_path"].isNull() || !layer_root_node["library_path"].isString() ||
811
layer_root_node["implementation_version"].isNull() || !layer_root_node["implementation_version"].isString()) {
812
error_ss << filename << " is missing required fields. Verify all proper fields exist.";
813
LoaderLogger::LogErrorMessage("", error_ss.str());
814
return;
815
}
816
if (MANIFEST_TYPE_IMPLICIT_API_LAYER == type) {
817
bool enabled = true;
818
// Implicit layers require the disable environment variable.
819
if (layer_root_node["disable_environment"].isNull() || !layer_root_node["disable_environment"].isString()) {
820
error_ss << "Implicit layer " << filename << " is missing \"disable_environment\"";
821
LoaderLogger::LogErrorMessage("", error_ss.str());
822
return;
823
}
824
// Check if there's an enable environment variable provided
825
if (!layer_root_node["enable_environment"].isNull() && layer_root_node["enable_environment"].isString()) {
826
std::string env_var = layer_root_node["enable_environment"].asString();
827
// If it's not set in the environment, disable the layer
828
if (!PlatformUtilsGetEnvSet(env_var.c_str())) {
829
enabled = false;
830
}
831
}
832
// Check for the disable environment variable, which must be provided in the JSON
833
std::string env_var = layer_root_node["disable_environment"].asString();
834
// If the env var is set, disable the layer. Disable env var overrides enable above
835
if (PlatformUtilsGetEnvSet(env_var.c_str())) {
836
enabled = false;
837
}
838
839
// Not enabled, so pretend like it isn't even there.
840
if (!enabled) {
841
error_ss << "Implicit layer " << filename << " is disabled";
842
LoaderLogger::LogInfoMessage("", error_ss.str());
843
return;
844
}
845
}
846
std::string layer_name = layer_root_node["name"].asString();
847
std::string api_version_string = layer_root_node["api_version"].asString();
848
JsonVersion api_version = {};
849
const int num_fields = sscanf(api_version_string.c_str(), "%u.%u", &api_version.major, &api_version.minor);
850
api_version.patch = 0;
851
852
if ((num_fields != 2) || (api_version.major == 0 && api_version.minor == 0) ||
853
api_version.major > XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) {
854
error_ss << "layer " << filename << " has invalid API Version. Skipping layer.";
855
LoaderLogger::LogWarningMessage("", error_ss.str());
856
return;
857
}
858
859
uint32_t implementation_version = atoi(layer_root_node["implementation_version"].asString().c_str());
860
std::string library_path = layer_root_node["library_path"].asString();
861
862
// If the library_path variable has no directory symbol, it's just a file name and should be accessible on the
863
// global library path.
864
if (library_path.find('\\') != std::string::npos || library_path.find('/') != std::string::npos) {
865
// If the library_path is an absolute path, just use that if it exists
866
if (FileSysUtilsIsAbsolutePath(library_path)) {
867
if (!FileSysUtilsPathExists(library_path)) {
868
error_ss << filename << " library " << library_path << " does not appear to exist";
869
LoaderLogger::LogErrorMessage("", error_ss.str());
870
return;
871
}
872
} else {
873
// Otherwise, treat the library path as a relative path based on the JSON file.
874
std::string combined_path;
875
if (!locate_library(filename, library_path, combined_path)) {
876
error_ss << filename << " library " << combined_path << " does not appear to exist";
877
LoaderLogger::LogErrorMessage("", error_ss.str());
878
return;
879
}
880
library_path = combined_path;
881
}
882
}
883
884
std::string description;
885
if (!layer_root_node["description"].isNull() && layer_root_node["description"].isString()) {
886
description = layer_root_node["description"].asString();
887
}
888
889
// Add this layer manifest file
890
manifest_files.emplace_back(
891
new ApiLayerManifestFile(type, filename, layer_name, description, api_version, implementation_version, library_path));
892
893
// Add any extensions to it after the fact.
894
manifest_files.back()->ParseCommon(layer_root_node);
895
}
896
897
void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename,
898
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
899
std::ifstream json_stream(filename, std::ifstream::in);
900
if (!json_stream.is_open()) {
901
std::ostringstream error_ss("ApiLayerManifestFile::CreateIfValid ");
902
error_ss << "failed to open " << filename << ". Does it exist?";
903
LoaderLogger::LogErrorMessage("", error_ss.str());
904
return;
905
}
906
CreateIfValid(type, filename, json_stream, &ApiLayerManifestFile::LocateLibraryRelativeToJson, manifest_files);
907
}
908
909
bool ApiLayerManifestFile::LocateLibraryRelativeToJson(
910
const std::string &json_filename, const std::string &library_path,
911
std::string &out_combined_path) { // Otherwise, treat the library path as a relative path based on the JSON file.
912
std::string combined_path;
913
std::string file_parent;
914
if (!FileSysUtilsGetParentPath(json_filename, file_parent) ||
915
!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
916
out_combined_path = combined_path;
917
return false;
918
}
919
out_combined_path = combined_path;
920
return true;
921
}
922
923
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
924
bool ApiLayerManifestFile::LocateLibraryInAssets(const std::string & /* json_filename */, const std::string &library_path,
925
std::string &out_combined_path) {
926
std::string combined_path;
927
std::string file_parent = GetAndroidNativeLibraryDir();
928
if (!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
929
out_combined_path = combined_path;
930
return false;
931
}
932
out_combined_path = combined_path;
933
return true;
934
}
935
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
936
937
void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &props) const {
938
props.layerVersion = _implementation_version;
939
props.specVersion = XR_MAKE_VERSION(_api_version.major, _api_version.minor, _api_version.patch);
940
strncpy(props.layerName, _layer_name.c_str(), XR_MAX_API_LAYER_NAME_SIZE - 1);
941
if (_layer_name.size() >= XR_MAX_API_LAYER_NAME_SIZE - 1) {
942
props.layerName[XR_MAX_API_LAYER_NAME_SIZE - 1] = '\0';
943
}
944
strncpy(props.description, _description.c_str(), XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1);
945
if (_description.size() >= XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1) {
946
props.description[XR_MAX_API_LAYER_DESCRIPTION_SIZE - 1] = '\0';
947
}
948
}
949
950
// Find all layer manifest files in the appropriate search paths/registries for the given type.
951
XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_command, ManifestFileType type,
952
std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
953
std::string relative_path;
954
std::string override_env_var;
955
std::string registry_location;
956
957
// Add the appropriate top-level folders for the relative path. These should be
958
// the string "openxr/" followed by the API major version as a string.
959
relative_path = OPENXR_RELATIVE_PATH;
960
relative_path += std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION));
961
962
switch (type) {
963
case MANIFEST_TYPE_IMPLICIT_API_LAYER:
964
relative_path += OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH;
965
override_env_var = "";
966
#ifdef XR_OS_WINDOWS
967
registry_location = OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION;
968
#endif
969
break;
970
case MANIFEST_TYPE_EXPLICIT_API_LAYER:
971
relative_path += OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH;
972
override_env_var = OPENXR_API_LAYER_PATH_ENV_VAR;
973
#ifdef XR_OS_WINDOWS
974
registry_location = OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION;
975
#endif
976
break;
977
default:
978
LoaderLogger::LogErrorMessage(openxr_command,
979
"ApiLayerManifestFile::FindManifestFiles - unknown manifest file requested");
980
return XR_ERROR_FILE_ACCESS_ERROR;
981
}
982
983
bool override_active = false;
984
std::vector<std::string> filenames;
985
ReadDataFilesInSearchPaths(override_env_var, relative_path, override_active, filenames);
986
987
#ifdef XR_OS_WINDOWS
988
// Read the registry if the override wasn't active.
989
if (!override_active) {
990
ReadLayerDataFilesInRegistry(registry_location, filenames);
991
}
992
#endif
993
994
for (std::string &cur_file : filenames) {
995
ApiLayerManifestFile::CreateIfValid(type, cur_file, manifest_files);
996
}
997
998
#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
999
ApiLayerManifestFile::AddManifestFilesAndroid(openxr_command, type, manifest_files);
1000
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
1001
1002
return XR_SUCCESS;
1003
}
1004
1005