godot/thirdparty/openxr/src/loader/manifest_file.cpp

// Copyright (c) 2017-2024, 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_init_data.hpp"
#include "loader_platform.hpp"
#include "platform_utils.hpp"
#include "loader_logger.hpp"
#include "unique_asset.h"

#include <json/json.h>
#include <openxr/openxr.h>

#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#ifndef FALLBACK_CONFIG_DIRS
#define FALLBACK_CONFIG_DIRS
#endif  // !FALLBACK_CONFIG_DIRS

#ifndef FALLBACK_DATA_DIRS
#define FALLBACK_DATA_DIRS
#endif  // !FALLBACK_DATA_DIRS

#ifndef SYSCONFDIR
#define SYSCONFDIR
#endif  // !SYSCONFDIR

#ifdef XR_USE_PLATFORM_ANDROID
#include <android/asset_manager.h>
#endif

#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 the file found is a manifest file name, add it to the out_files manifest list.
static bool AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {}

// 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) {}

// 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) {}

// 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) {}

// 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) {}

#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) {}

// Get an XDG environment variable with absolute defaults
static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_paths) {}

/// @param rt_dir_prefix Directory prefix with a trailing slash
static bool FindEitherActiveRuntimeFilename(const char *prefix_desc, const std::string &rt_dir_prefix, uint16_t major_version,
                                            std::string &out) {}
// Return the first instance of relative_path occurring in an XDG config dir according to standard
// precedence order.
static bool FindXDGConfigFile(const char *relative_dir, uint16_t major_version, std::string &out) {}

#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);
        return;
    }

    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 {
        // Not using AddFilesInPath here (as only api_layer manifest paths allow multiple
        // separated paths)
        // Small time-of-check vs time-of-use issue here but it mainly only affects the error message.
        // It does not introduce a security defect.
        std::string activeRuntimePath = wide_to_utf8(value_w);
        if (FileSysUtilsIsRegularFile(activeRuntimePath)) {
            // If the file exists, try to add it
            std::string absolute_path;
            FileSysUtilsGetAbsolutePath(activeRuntimePath, absolute_path);
            if (!AddIfJson(absolute_path, manifest_files)) {
                LoaderLogger::LogErrorMessage(
                    "", "ReadRuntimeDataFilesInRegistry - registry runtime path is not json " + activeRuntimePath);
            }
        } else {
            LoaderLogger::LogErrorMessage(
                "", "ReadRuntimeDataFilesInRegistry - registry runtime path does not exist " + activeRuntimePath);
        }
    }

    RegCloseKey(hkey);
}

// 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 &registry_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)
    :{}

bool ManifestFile::IsValidJson(const Json::Value &root_node, JsonVersion &version) {}

static void GetExtensionProperties(const std::vector<ExtensionListing> &extensions, std::vector<XrExtensionProperties> &props) {}

// Return any instance extensions found in the manifest files in the proper form for
// OpenXR (XrExtensionProperties).
void ManifestFile::GetInstanceExtensionProperties(std::vector<XrExtensionProperties> &props) {}

const std::string &ManifestFile::GetFunctionName(const std::string &func_name) const {}

RuntimeManifestFile::RuntimeManifestFile(const std::string &filename, const std::string &library_path)
    :{}

static void ParseExtension(Json::Value const &ext, std::vector<ExtensionListing> &extensions) {}

void ManifestFile::ParseCommon(Json::Value const &root_node) {}

void RuntimeManifestFile::CreateIfValid(std::string const &filename,
                                        std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {}

void RuntimeManifestFile::CreateIfValid(const Json::Value &root_node, const std::string &filename,
                                        std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {}

// Find all manifest files in the appropriate search paths/registries for the given type.
XrResult RuntimeManifestFile::FindManifestFiles(const std::string &openxr_command,
                                                std::vector<std::unique_ptr<RuntimeManifestFile>> &manifest_files) {}

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)
    :{}

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_command, ManifestFileType type,
                                                   std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {
    if (!LoaderInitData::instance().initialized()) {
        // This will happen for applications that do not call xrInitializeLoaderKHR
        LoaderLogger::LogWarningMessage(
            openxr_command,
            "ApiLayerManifestFile::AddManifestFilesAndroid unable to add manifest files LoaderInitData not initialized.");
        return;
    }

    AAssetManager *assetManager = (AAssetManager *)Android_Get_Asset_Manager();
    std::vector<std::string> filenames;
    {
        std::string search_path = "";
        switch (type) {
            case MANIFEST_TYPE_IMPLICIT_API_LAYER:
                search_path = "openxr/1/api_layers/implicit.d/";
                break;
            case MANIFEST_TYPE_EXPLICIT_API_LAYER:
                search_path = "openxr/1/api_layers/explicit.d/";
                break;
            default:
                return;
        }

        UniqueAssetDir dir{AAssetManager_openDir(assetManager, search_path.c_str())};
        if (!dir) {
            return;
        }
        const std::string json = ".json";
        const char *fn = nullptr;
        while ((fn = AAssetDir_getNextFileName(dir.get())) != nullptr) {
            const std::string filename = search_path + fn;
            if (filename.size() < json.size()) {
                continue;
            }
            if (filename.compare(filename.size() - json.size(), json.size(), json) == 0) {
                filenames.push_back(filename);
            }
        }
    }
    for (const auto &filename : filenames) {
        UniqueAsset asset{AAssetManager_open(assetManager, filename.c_str(), AASSET_MODE_BUFFER)};
        if (!asset) {
            LoaderLogger::LogWarningMessage(
                openxr_command, "ApiLayerManifestFile::AddManifestFilesAndroid unable to open asset " + filename + ", skipping");

            continue;
        }
        size_t length = AAsset_getLength(asset.get());
        const char *buf = reinterpret_cast<const char *>(AAsset_getBuffer(asset.get()));
        if (!buf) {
            LoaderLogger::LogWarningMessage(
                openxr_command, "ApiLayerManifestFile::AddManifestFilesAndroid unable to access asset" + filename + ", skipping");

            continue;
        }
        std::istringstream json_stream(std::string{buf, length});

        CreateIfValid(type, filename, json_stream, &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files);
    }
}
#endif  // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename, std::istream &json_stream,
                                         LibraryLocator locate_library,
                                         std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {}

void ApiLayerManifestFile::CreateIfValid(ManifestFileType type, const std::string &filename,
                                         std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {}

bool ApiLayerManifestFile::LocateLibraryRelativeToJson(
    const std::string &json_filename, const std::string &library_path,
    std::string &out_combined_path) {}

#if defined(XR_KHR_LOADER_INIT_SUPPORT) && defined(XR_USE_PLATFORM_ANDROID)
bool ApiLayerManifestFile::LocateLibraryInAssets(const std::string & /* json_filename */, const std::string &library_path,
                                                 std::string &out_combined_path) {
    std::string combined_path;
    std::string file_parent = GetAndroidNativeLibraryDir();
    if (!FileSysUtilsCombinePaths(file_parent, library_path, combined_path) || !FileSysUtilsPathExists(combined_path)) {
        out_combined_path = combined_path;
        return false;
    }
    out_combined_path = combined_path;
    return true;
}
#endif  // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)

void ApiLayerManifestFile::PopulateApiLayerProperties(XrApiLayerProperties &props) const {}

// Find all layer manifest files in the appropriate search paths/registries for the given type.
XrResult ApiLayerManifestFile::FindManifestFiles(const std::string &openxr_command, ManifestFileType type,
                                                 std::vector<std::unique_ptr<ApiLayerManifestFile>> &manifest_files) {}