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