15db71995Sopenharmony_ci/*
25db71995Sopenharmony_ci * Copyright (c) 2021 The Khronos Group Inc.
35db71995Sopenharmony_ci * Copyright (c) 2021 Valve Corporation
45db71995Sopenharmony_ci * Copyright (c) 2021 LunarG, Inc.
55db71995Sopenharmony_ci *
65db71995Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
75db71995Sopenharmony_ci * of this software and/or associated documentation files (the "Materials"), to
85db71995Sopenharmony_ci * deal in the Materials without restriction, including without limitation the
95db71995Sopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
105db71995Sopenharmony_ci * sell copies of the Materials, and to permit persons to whom the Materials are
115db71995Sopenharmony_ci * furnished to do so, subject to the following conditions:
125db71995Sopenharmony_ci *
135db71995Sopenharmony_ci * The above copyright notice(s) and this permission notice shall be included in
145db71995Sopenharmony_ci * all copies or substantial portions of the Materials.
155db71995Sopenharmony_ci *
165db71995Sopenharmony_ci * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
175db71995Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
185db71995Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
195db71995Sopenharmony_ci *
205db71995Sopenharmony_ci * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
215db71995Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
225db71995Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
235db71995Sopenharmony_ci * USE OR OTHER DEALINGS IN THE MATERIALS.
245db71995Sopenharmony_ci *
255db71995Sopenharmony_ci * Author: Charles Giessen <charles@lunarg.com>
265db71995Sopenharmony_ci */
275db71995Sopenharmony_ci
285db71995Sopenharmony_ci#include "shim.h"
295db71995Sopenharmony_ci
305db71995Sopenharmony_ci#include <random>
315db71995Sopenharmony_ci
325db71995Sopenharmony_civoid PlatformShim::redirect_all_paths(fs::path const& path) {
335db71995Sopenharmony_ci    redirect_category(path, ManifestCategory::implicit_layer);
345db71995Sopenharmony_ci    redirect_category(path, ManifestCategory::explicit_layer);
355db71995Sopenharmony_ci    redirect_category(path, ManifestCategory::icd);
365db71995Sopenharmony_ci}
375db71995Sopenharmony_ci
385db71995Sopenharmony_cistd::vector<std::string> parse_env_var_list(std::string const& var) {
395db71995Sopenharmony_ci    std::vector<std::string> items;
405db71995Sopenharmony_ci    size_t start = 0;
415db71995Sopenharmony_ci    size_t len = 0;
425db71995Sopenharmony_ci    for (size_t i = 0; i < var.size(); i++) {
435db71995Sopenharmony_ci#if defined(WIN32)
445db71995Sopenharmony_ci        if (var[i] == ';') {
455db71995Sopenharmony_ci#elif COMMON_UNIX_PLATFORMS
465db71995Sopenharmony_ci        if (var[i] == ':') {
475db71995Sopenharmony_ci#endif
485db71995Sopenharmony_ci            if (len != 0) {
495db71995Sopenharmony_ci                // only push back non empty strings
505db71995Sopenharmony_ci                items.push_back(var.substr(start, len));
515db71995Sopenharmony_ci            }
525db71995Sopenharmony_ci            start = i + 1;
535db71995Sopenharmony_ci            len = 0;
545db71995Sopenharmony_ci        } else {
555db71995Sopenharmony_ci            len++;
565db71995Sopenharmony_ci        }
575db71995Sopenharmony_ci    }
585db71995Sopenharmony_ci    items.push_back(var.substr(start, len));
595db71995Sopenharmony_ci
605db71995Sopenharmony_ci    return items;
615db71995Sopenharmony_ci}
625db71995Sopenharmony_ci
635db71995Sopenharmony_cistd::vector<std::string> get_folder_contents(std::vector<fs::FolderManager>* folders, std::string folder_name) noexcept {
645db71995Sopenharmony_ci    for (auto& folder : *folders) {
655db71995Sopenharmony_ci        if (folder.location() == folder_name) {
665db71995Sopenharmony_ci            return folder.get_files();
675db71995Sopenharmony_ci        }
685db71995Sopenharmony_ci    }
695db71995Sopenharmony_ci    return {};
705db71995Sopenharmony_ci}
715db71995Sopenharmony_ci
725db71995Sopenharmony_ci#if defined(WIN32)
735db71995Sopenharmony_ci
745db71995Sopenharmony_ciD3DKMT_Adapter& D3DKMT_Adapter::add_driver_manifest_path(fs::path const& src) { return add_path(src, driver_paths); }
755db71995Sopenharmony_ciD3DKMT_Adapter& D3DKMT_Adapter::add_implicit_layer_manifest_path(fs::path const& src) {
765db71995Sopenharmony_ci    return add_path(src, implicit_layer_paths);
775db71995Sopenharmony_ci}
785db71995Sopenharmony_ciD3DKMT_Adapter& D3DKMT_Adapter::add_explicit_layer_manifest_path(fs::path const& src) {
795db71995Sopenharmony_ci    return add_path(src, explicit_layer_paths);
805db71995Sopenharmony_ci}
815db71995Sopenharmony_ci
825db71995Sopenharmony_ciD3DKMT_Adapter& D3DKMT_Adapter::add_path(fs::path src, std::vector<std::wstring>& dest) {
835db71995Sopenharmony_ci    std::wstring dest_path;
845db71995Sopenharmony_ci    dest_path.resize(src.size());
855db71995Sopenharmony_ci    MultiByteToWideChar(CP_UTF8, 0, src.c_str(), static_cast<int>(src.size()), &dest_path[0], static_cast<int>(dest_path.size()));
865db71995Sopenharmony_ci    dest.push_back(dest_path);
875db71995Sopenharmony_ci    return *this;
885db71995Sopenharmony_ci}
895db71995Sopenharmony_ci
905db71995Sopenharmony_cistd::string category_path_name(ManifestCategory category) {
915db71995Sopenharmony_ci    if (category == ManifestCategory::implicit_layer) return "ImplicitLayers";
925db71995Sopenharmony_ci    if (category == ManifestCategory::explicit_layer)
935db71995Sopenharmony_ci        return "ExplicitLayers";
945db71995Sopenharmony_ci    else
955db71995Sopenharmony_ci        return "Drivers";
965db71995Sopenharmony_ci}
975db71995Sopenharmony_ci
985db71995Sopenharmony_civoid PlatformShim::reset() {
995db71995Sopenharmony_ci    hkey_current_user_explicit_layers.clear();
1005db71995Sopenharmony_ci    hkey_current_user_implicit_layers.clear();
1015db71995Sopenharmony_ci    hkey_local_machine_explicit_layers.clear();
1025db71995Sopenharmony_ci    hkey_local_machine_implicit_layers.clear();
1035db71995Sopenharmony_ci    hkey_local_machine_drivers.clear();
1045db71995Sopenharmony_ci    hkey_local_machine_settings.clear();
1055db71995Sopenharmony_ci    hkey_current_user_settings.clear();
1065db71995Sopenharmony_ci}
1075db71995Sopenharmony_ci
1085db71995Sopenharmony_civoid PlatformShim::set_fake_path([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {}
1095db71995Sopenharmony_civoid PlatformShim::add_known_path([[maybe_unused]] fs::path const& path) {}
1105db71995Sopenharmony_ci
1115db71995Sopenharmony_civoid PlatformShim::add_manifest(ManifestCategory category, fs::path const& path) {
1125db71995Sopenharmony_ci    if (category == ManifestCategory::settings) {
1135db71995Sopenharmony_ci        hkey_local_machine_settings.emplace_back(path.str());
1145db71995Sopenharmony_ci    } else if (category == ManifestCategory::implicit_layer) {
1155db71995Sopenharmony_ci        hkey_local_machine_implicit_layers.emplace_back(path.str());
1165db71995Sopenharmony_ci    } else if (category == ManifestCategory::explicit_layer) {
1175db71995Sopenharmony_ci        hkey_local_machine_explicit_layers.emplace_back(path.str());
1185db71995Sopenharmony_ci    } else {
1195db71995Sopenharmony_ci        hkey_local_machine_drivers.emplace_back(path.str());
1205db71995Sopenharmony_ci    }
1215db71995Sopenharmony_ci}
1225db71995Sopenharmony_ci
1235db71995Sopenharmony_civoid PlatformShim::add_unsecured_manifest(ManifestCategory category, fs::path const& path) {
1245db71995Sopenharmony_ci    if (category == ManifestCategory::settings) {
1255db71995Sopenharmony_ci        hkey_current_user_settings.emplace_back(path.str());
1265db71995Sopenharmony_ci    } else if (category == ManifestCategory::implicit_layer) {
1275db71995Sopenharmony_ci        hkey_current_user_implicit_layers.emplace_back(path.str());
1285db71995Sopenharmony_ci    } else if (category == ManifestCategory::explicit_layer) {
1295db71995Sopenharmony_ci        hkey_current_user_explicit_layers.emplace_back(path.str());
1305db71995Sopenharmony_ci    }
1315db71995Sopenharmony_ci}
1325db71995Sopenharmony_ci
1335db71995Sopenharmony_civoid PlatformShim::add_dxgi_adapter(GpuType gpu_preference, DXGI_ADAPTER_DESC1 desc1) {
1345db71995Sopenharmony_ci    uint32_t next_index = static_cast<uint32_t>(dxgi_adapters.size());
1355db71995Sopenharmony_ci    dxgi_adapters.emplace(next_index, DXGIAdapter{gpu_preference, desc1, next_index});
1365db71995Sopenharmony_ci}
1375db71995Sopenharmony_ci
1385db71995Sopenharmony_civoid PlatformShim::add_d3dkmt_adapter(D3DKMT_Adapter const& adapter) { d3dkmt_adapters.push_back(adapter); }
1395db71995Sopenharmony_ci
1405db71995Sopenharmony_civoid PlatformShim::set_app_package_path(fs::path const& path) {
1415db71995Sopenharmony_ci    app_package_path.resize(path.size());
1425db71995Sopenharmony_ci    MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, &app_package_path[0], static_cast<int>(app_package_path.size()));
1435db71995Sopenharmony_ci}
1445db71995Sopenharmony_ci
1455db71995Sopenharmony_ci// TODO:
1465db71995Sopenharmony_civoid PlatformShim::add_CM_Device_ID([[maybe_unused]] std::wstring const& id, [[maybe_unused]] fs::path const& icd_path,
1475db71995Sopenharmony_ci                                    [[maybe_unused]] fs::path const& layer_path) {
1485db71995Sopenharmony_ci    //     // append a null byte as separator if there is already id's in the list
1495db71995Sopenharmony_ci    //     if (CM_device_ID_list.size() != 0) {
1505db71995Sopenharmony_ci    //         CM_device_ID_list += L'\0';  // I'm sure this wont cause issues with std::string down the line... /s
1515db71995Sopenharmony_ci    //     }
1525db71995Sopenharmony_ci    //     CM_device_ID_list += id;
1535db71995Sopenharmony_ci    //     std::string id_str(id.length(), '\0');
1545db71995Sopenharmony_ci    //     size_t size_written{};
1555db71995Sopenharmony_ci    //     wcstombs_s(&size_written, &id_str[0], id_str.length(), id.c_str(), id.length());
1565db71995Sopenharmony_ci
1575db71995Sopenharmony_ci    //     std::string device_path = std::string(pnp_registry_path) + "\\" + id_str;
1585db71995Sopenharmony_ci    //     CM_device_ID_registry_keys.push_back(device_path.c_str());
1595db71995Sopenharmony_ci    //     add_key_value_string(id_key, "VulkanDriverName", icd_path.c_str());
1605db71995Sopenharmony_ci    //     add_key_value_string(id_key, "VulkanLayerName", layer_path.c_str());
1615db71995Sopenharmony_ci    //     // TODO: decide how to handle 32 bit
1625db71995Sopenharmony_ci    //     // add_key_value_string(id_key, "VulkanDriverNameWoW", icd_path.c_str());
1635db71995Sopenharmony_ci    //     // add_key_value_string(id_key, "VulkanLayerName", layer_path.c_str());
1645db71995Sopenharmony_ci}
1655db71995Sopenharmony_ci
1665db71995Sopenharmony_civoid PlatformShim::redirect_category(fs::path const&, ManifestCategory) {}
1675db71995Sopenharmony_ci
1685db71995Sopenharmony_ci#elif COMMON_UNIX_PLATFORMS
1695db71995Sopenharmony_ci
1705db71995Sopenharmony_ci#include <dirent.h>
1715db71995Sopenharmony_ci#include <unistd.h>
1725db71995Sopenharmony_ci
1735db71995Sopenharmony_cistd::string category_path_name(ManifestCategory category) {
1745db71995Sopenharmony_ci    if (category == ManifestCategory::settings) return "settings.d";
1755db71995Sopenharmony_ci    if (category == ManifestCategory::implicit_layer) return "implicit_layer.d";
1765db71995Sopenharmony_ci    if (category == ManifestCategory::explicit_layer)
1775db71995Sopenharmony_ci        return "explicit_layer.d";
1785db71995Sopenharmony_ci    else
1795db71995Sopenharmony_ci        return "icd.d";
1805db71995Sopenharmony_ci}
1815db71995Sopenharmony_ci
1825db71995Sopenharmony_civoid PlatformShim::reset() { redirection_map.clear(); }
1835db71995Sopenharmony_ci
1845db71995Sopenharmony_cibool PlatformShim::is_fake_path(fs::path const& path) { return redirection_map.count(path.str()) > 0; }
1855db71995Sopenharmony_cifs::path const& PlatformShim::get_real_path_from_fake_path(fs::path const& path) { return redirection_map.at(path.str()); }
1865db71995Sopenharmony_civoid PlatformShim::redirect_path(fs::path const& path, fs::path const& new_path) { redirection_map[path.str()] = new_path; }
1875db71995Sopenharmony_civoid PlatformShim::remove_redirect(fs::path const& path) { redirection_map.erase(path.str()); }
1885db71995Sopenharmony_ci
1895db71995Sopenharmony_cibool PlatformShim::is_known_path(fs::path const& path) { return known_path_set.count(path.str()) > 0; }
1905db71995Sopenharmony_civoid PlatformShim::add_known_path(fs::path const& path) { known_path_set.insert(path.str()); }
1915db71995Sopenharmony_civoid PlatformShim::remove_known_path(fs::path const& path) { known_path_set.erase(path.str()); }
1925db71995Sopenharmony_ci
1935db71995Sopenharmony_civoid PlatformShim::add_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {}
1945db71995Sopenharmony_civoid PlatformShim::add_unsecured_manifest([[maybe_unused]] ManifestCategory category, [[maybe_unused]] fs::path const& path) {}
1955db71995Sopenharmony_ci
1965db71995Sopenharmony_civoid parse_and_add_env_var_override(std::vector<std::string>& paths, std::string env_var_contents) {
1975db71995Sopenharmony_ci    auto parsed_paths = parse_env_var_list(env_var_contents);
1985db71995Sopenharmony_ci    paths.insert(paths.end(), parsed_paths.begin(), parsed_paths.end());
1995db71995Sopenharmony_ci}
2005db71995Sopenharmony_ci
2015db71995Sopenharmony_civoid PlatformShim::redirect_category(fs::path const& new_path, ManifestCategory category) {
2025db71995Sopenharmony_ci    std::vector<std::string> paths;
2035db71995Sopenharmony_ci    auto home = fs::path(get_env_var("HOME"));
2045db71995Sopenharmony_ci    if (category == ManifestCategory::settings) {
2055db71995Sopenharmony_ci        redirect_path(home / ".local/share/vulkan" / category_path_name(category), new_path);
2065db71995Sopenharmony_ci        return;
2075db71995Sopenharmony_ci    }
2085db71995Sopenharmony_ci
2095db71995Sopenharmony_ci    if (home.size() != 0) {
2105db71995Sopenharmony_ci        paths.push_back((home / ".config").str());
2115db71995Sopenharmony_ci        paths.push_back((home / ".local/share").str());
2125db71995Sopenharmony_ci    }
2135db71995Sopenharmony_ci    // Don't report errors on apple - these env-vars are not suppose to be defined
2145db71995Sopenharmony_ci    bool report_errors = true;
2155db71995Sopenharmony_ci#if defined(__APPLE__)
2165db71995Sopenharmony_ci    report_errors = false;
2175db71995Sopenharmony_ci#endif
2185db71995Sopenharmony_ci    parse_and_add_env_var_override(paths, get_env_var("XDG_CONFIG_HOME", report_errors));
2195db71995Sopenharmony_ci    if (category == ManifestCategory::explicit_layer) {
2205db71995Sopenharmony_ci        parse_and_add_env_var_override(paths, get_env_var("VK_LAYER_PATH", false));  // don't report failure
2215db71995Sopenharmony_ci    }
2225db71995Sopenharmony_ci    parse_and_add_env_var_override(paths, FALLBACK_DATA_DIRS);
2235db71995Sopenharmony_ci    parse_and_add_env_var_override(paths, FALLBACK_CONFIG_DIRS);
2245db71995Sopenharmony_ci
2255db71995Sopenharmony_ci    auto sys_conf_dir = std::string(SYSCONFDIR);
2265db71995Sopenharmony_ci    if (!sys_conf_dir.empty()) {
2275db71995Sopenharmony_ci        paths.push_back(sys_conf_dir);
2285db71995Sopenharmony_ci    }
2295db71995Sopenharmony_ci#if defined(EXTRASYSCONFDIR)
2305db71995Sopenharmony_ci    // EXTRASYSCONFDIR default is /etc, if SYSCONFDIR wasn't defined, it will have /etc put
2315db71995Sopenharmony_ci    // as its default. Don't want to double add it
2325db71995Sopenharmony_ci    auto extra_sys_conf_dir = std::string(EXTRASYSCONFDIR);
2335db71995Sopenharmony_ci    if (!extra_sys_conf_dir.empty() && sys_conf_dir != extra_sys_conf_dir) {
2345db71995Sopenharmony_ci        paths.push_back(extra_sys_conf_dir);
2355db71995Sopenharmony_ci    }
2365db71995Sopenharmony_ci#endif
2375db71995Sopenharmony_ci
2385db71995Sopenharmony_ci    for (auto& path : paths) {
2395db71995Sopenharmony_ci        if (!path.empty()) {
2405db71995Sopenharmony_ci            redirect_path(fs::path(path) / "vulkan" / category_path_name(category), new_path);
2415db71995Sopenharmony_ci        }
2425db71995Sopenharmony_ci    }
2435db71995Sopenharmony_ci}
2445db71995Sopenharmony_ci
2455db71995Sopenharmony_civoid PlatformShim::set_fake_path(ManifestCategory category, fs::path const& path) {
2465db71995Sopenharmony_ci    // use /etc as the 'redirection path' by default since its always searched
2475db71995Sopenharmony_ci    redirect_path(fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category), path);
2485db71995Sopenharmony_ci}
2495db71995Sopenharmony_ci
2505db71995Sopenharmony_civoid PlatformShim::redirect_dlopen_name(fs::path const& filename, fs::path const& actual_path) {
2515db71995Sopenharmony_ci    dlopen_redirection_map[filename.str()] = actual_path;
2525db71995Sopenharmony_ci}
2535db71995Sopenharmony_ci
2545db71995Sopenharmony_cibool PlatformShim::is_dlopen_redirect_name(fs::path const& filename) { return dlopen_redirection_map.count(filename.str()) == 1; }
2555db71995Sopenharmony_ci
2565db71995Sopenharmony_cifs::path PlatformShim::query_default_redirect_path(ManifestCategory category) {
2575db71995Sopenharmony_ci    return fs::path(SYSCONFDIR) / "vulkan" / category_path_name(category);
2585db71995Sopenharmony_ci}
2595db71995Sopenharmony_ci#endif
260