| /****************************************************************************** | |
| * Copyright (c) 2016 The Khronos Group | |
| * Copyright (c) 2016 Valve Corporation | |
| * Copyright (c) 2016 LunarG, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you man not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * https://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is destributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language govering permissions and | |
| * limitations under the License. | |
| * | |
| * Author: Lenny Komow <lenny@lunarg.com> | |
| * | |
| *****************************************************************************/ | |
| /* | |
| * This program is used by the Vulkan Runtime Installer/Uninstaller to: | |
| * - Copy the most recent vulkan<majorabi>-*.dll in C:\Windows\System32 | |
| * to vulkan<majorabi>.dll | |
| * - Copy the most recent version of vulkaninfo-<abimajor>-*.exe in | |
| * C:\Windows\System32 to vulkaninfo.exe | |
| * - The same thing is done for those files in C:\Windows\SysWOW64, but | |
| * only on a 64-bit target | |
| * - Set the layer registry entried to point to the layer json files in | |
| * the Vulkan SDK associated with the most recent vulkan*.dll | |
| * | |
| * The program must be called with the following parameters: | |
| * --major-abi: A single number specifying the major abi version | |
| */ | |
| // Compile with: `cl.exe configure_runtime.c /link advapi32.lib` | |
| // Be sure to use the x86 version of cl.exe | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <windows.h> | |
| // This hack gets Visual Studio 2013 to handle C99 stuff properly | |
| // If we drop support for 2013, it would be a good idea to remove this | |
| #if _MSC_VER < 1900 | |
| #define inline __inline | |
| #define snprintf _snprintf | |
| #endif | |
| #if defined(_WIN64) | |
| #error "This program is designed only as a 32-bit program. It should not be built as 64-bit." | |
| #endif | |
| #define COPY_BUFFER_SIZE (1024) | |
| #define CHECK_ERROR(statement) { int error = (statement); if(error) return error; } | |
| #define CHECK_ERROR_HANDLED(statement, handler) { int error = (statement); if(error) { { handler } return error; } } | |
| #define SDK_VERSION_BUFFER_SIZE (64) | |
| enum Platform | |
| { | |
| PLATFORM_X64, | |
| PLATFORM_X86, | |
| }; | |
| #pragma pack(1) | |
| struct SDKVersion | |
| { | |
| long major; | |
| long minor; | |
| long patch; | |
| long build; | |
| char extended[SDK_VERSION_BUFFER_SIZE]; | |
| }; | |
| const char* FLAG_ABI_MAJOR = "--abi-major"; | |
| const char* FLAG_API_NAME = "--api-name"; | |
| const char* PATH_SYSTEM32 = "\\SYSTEM32\\"; | |
| const char* PATH_SYSWOW64 = "\\SysWOW64\\"; | |
| inline size_t max_s(size_t a, size_t b) { return a > b ? a : b; } | |
| inline size_t min_s(size_t a, size_t b) { return a > b ? a : b; } | |
| // Add the registry entries for all explicit layers | |
| // | |
| // log (input) - Logging file stream | |
| // install_path (input) - The installation path of the SDK which provides the layers | |
| // platform (input) - The platform to set the installation for (x64 or x86) | |
| // api_name (input) - The api name to use when working with registries | |
| // Returns: Zero on success, an error code on failure | |
| int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name); | |
| // Compare two sdk versions | |
| // | |
| // Returns: Zero if they are equal, below zero if a predates b, greater than zero if b predates a | |
| int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b); | |
| // Locate all of the SDK installations | |
| // | |
| // api_name (input) - The api name to use when working with registries | |
| // install_paths (output) - A poiner to an array of the installations paths | |
| // install_versions (output) - A pointer to an array of the SDK versions | |
| // count (output) - A pointer to the number of items in each array | |
| // Returns: Zero on success, an error code on failure | |
| // | |
| // Both install_paths and install_versions are allocated on the heap. To free them properly, | |
| // call free_installations(), even if this function returned an error code. The orders of | |
| // install_paths and install_versions match, so (*install_paths)[2] is guaranteed to match | |
| // (*install_versions)[2] | |
| int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions, | |
| size_t* count); | |
| // Free the memory allocated by find_installations() | |
| void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count); | |
| // Parse command line arguments for the program | |
| // | |
| // log (input) - Logging file stream | |
| // argc (input) - The argument count | |
| // argv (input) - An array of argument strings | |
| // abi_major (output) - The major abi version from the arguments | |
| // api_name (output) - The api name to use when working with registries and system files | |
| // Returns: Zero on success, an error code on failure | |
| int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name); | |
| // Read the version from a string | |
| // | |
| // version_string (input) - A string in the format <abi>.<major>.<minor>.<patch>.<build>.<extended> | |
| // version (output) - The version indicated by the input string | |
| // Returns: Zero on success, an error code on failure | |
| int read_version(const char* version_string, struct SDKVersion* version); | |
| // Read the version from a filename | |
| // | |
| // filename (input) - The name of a .dll or .exe file, in the format | |
| // somename-<abi>-<major>-<minor>-<path>-<build>-<extended>.dll | |
| // version (output) - The versions indicated by the input string | |
| // Returns: Zero on success, an error code on failure | |
| int read_version_from_filename(const char* filename, struct SDKVersion* version); | |
| // Remove explicit layers from the Windows registry | |
| // | |
| // log (input) - Loggin file stream | |
| // install_paths (input) - An array of every vulkan installation path | |
| // count (input) - The number of vulkan installations | |
| // platform (input) - The platform (x64 or x86) of the registry to use (both exist on x64) | |
| // api_name (input) - The api name to use when working with registries | |
| // Returns: Zero on success, an error code on failure | |
| int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform, | |
| const char* api_name); | |
| // Update all explicity layers in the windows registry | |
| // | |
| // log (input) - Logging file stream | |
| // platform (input) - The platform of the OS (both registries will be modified if this is x64) | |
| // version (input) - The version that should be set to current (if it exists) | |
| // api_name (input) - The api name to use when working with registries | |
| // Returns: Zero on success, an error code on failure | |
| int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version, | |
| const char* api_name); | |
| // Update a single vulkan system file (vulkan.dll or vulkaninfo.exe) | |
| // | |
| // log (input) - Loggin file stream | |
| // name (input) - The name (excuding file extension) of the file to be updated | |
| // extension (input) - The file extensions of the file to be updated | |
| // path (input) - The directory of the file (usually System32 or SysWOW64) | |
| // abi_major (input) - The ABI major version to be updated | |
| // append_abi_major (input) - Whether or not the ABI number should be appended to the filename | |
| // latest_version (output) - The version of the runtime which the file was updated to | |
| // Returns: Zero on success, an error code on failure | |
| int update_system_file(FILE* log, const char* name, const char* extension, const char* path, | |
| long abi_major, bool append_abi_major, struct SDKVersion* latest_version); | |
| // Update vulkan.dll and vulkaninfo.exe in all of the windows directories (System32 and SysWOW64) | |
| // | |
| // log (input) - Loging file stream | |
| // abi_major (input) - The ABI major version of the files that should be used | |
| // platform (input) - The platform for the current OS | |
| // api_name (input) - The api name to use when working with system files | |
| // latest_runtime_version (output) - The version that the runtime files were updated to | |
| int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name, | |
| struct SDKVersion* latest_runtime_version); | |
| int main(int argc, char** argv) | |
| { | |
| // Get the OS platform (x86 or x64) | |
| BOOL is_64_bit; | |
| IsWow64Process(GetCurrentProcess(), &is_64_bit); | |
| enum Platform platform = is_64_bit ? PLATFORM_X64 : PLATFORM_X86; | |
| FILE* log = fopen("configure_rt.log", "w"); | |
| if(log == NULL) { | |
| return 10; | |
| } | |
| // Parse the arguments to get the abi version and the number of bits of the OS | |
| long abi_major; | |
| const char* api_name; | |
| CHECK_ERROR_HANDLED(parse_arguments(log, argc, argv, &abi_major, &api_name), { fclose(log); }); | |
| fprintf(log, "API Name: %s\n", api_name); | |
| // This makes System32 and SysWOW64 not do any redirection (well, until 128-bit is a thing) | |
| PVOID useless; | |
| Wow64DisableWow64FsRedirection(&useless); | |
| // Update System32 (on all systems) and SysWOW64 on 64-bit system | |
| struct SDKVersion latest_runtime_version; | |
| CHECK_ERROR_HANDLED(update_windows_directories(log, abi_major, platform, api_name, | |
| &latest_runtime_version), { fclose(log); }); | |
| // Update the explicit layers that are set in the windows registry | |
| CHECK_ERROR_HANDLED(update_registry_layers(log, platform, &latest_runtime_version, api_name), | |
| { fclose(log); }); | |
| fclose(log); | |
| return 0; | |
| } | |
| int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name) | |
| { | |
| switch(platform) | |
| { | |
| case PLATFORM_X64: | |
| fprintf(log, "Updating x64 explicit layers to path: %s\n", install_path); | |
| break; | |
| case PLATFORM_X86: | |
| fprintf(log, "Updating x86 explicit layers to path: %s\n", install_path); | |
| break; | |
| } | |
| const char* registry_pattern = "SOFTWARE\\Khronos\\%s\\ExplicitLayers"; | |
| int registry_size = snprintf(NULL, 0, registry_pattern, api_name) + 1; | |
| char* registry_key = malloc(registry_size); | |
| snprintf(registry_key, registry_size, registry_pattern, api_name); | |
| // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries. | |
| // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node. | |
| HKEY hKey; | |
| REGSAM flags = KEY_ALL_ACCESS; | |
| if(platform == PLATFORM_X64) { | |
| flags |= KEY_WOW64_64KEY; | |
| } | |
| // Create (if needed) and open the explicit layer key | |
| if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags, | |
| NULL, &hKey, NULL) != ERROR_SUCCESS) { | |
| free(registry_key); | |
| return 20; | |
| } | |
| const char* pattern = platform == PLATFORM_X64 ? "%s\\Bin\\VkLayer*.json" : "%s\\Bin32\\VkLayer*.json"; | |
| int filter_size = snprintf(NULL, 0, pattern, install_path) + 1; | |
| if(filter_size < 0) { | |
| return 30; | |
| } | |
| char* filter = malloc(filter_size); | |
| snprintf(filter, filter_size, pattern, install_path); | |
| WIN32_FIND_DATA find_data; | |
| HANDLE find = FindFirstFile(filter, &find_data); | |
| free(filter); | |
| for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end; | |
| at_end = FindNextFile(find, &find_data)) { | |
| const char* layer_pattern = platform == PLATFORM_X64 ? "%s\\Bin\\%s" : "%s\\Bin32\\%s"; | |
| int layer_size = snprintf(NULL, 0, layer_pattern, install_path, find_data.cFileName) + 1; | |
| if(layer_size < 0) { | |
| free(registry_key); | |
| return 40; | |
| } | |
| char* layer = malloc(layer_size); | |
| snprintf(layer, layer_size, layer_pattern, install_path, find_data.cFileName); | |
| fprintf(log, "Adding explicit layer: %s\n", layer); | |
| DWORD zero = 0; | |
| LSTATUS err = RegSetValueEx(hKey, layer, zero, REG_DWORD, (BYTE*) &zero, sizeof(DWORD)); | |
| free(layer); | |
| if(err != ERROR_SUCCESS) { | |
| free(registry_key); | |
| return 50; | |
| } | |
| } | |
| RegCloseKey(hKey); | |
| free(registry_key); | |
| return 0; | |
| } | |
| int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b) | |
| { | |
| // Compare numerical versions | |
| for(int i = 0; i < 4; ++i) { | |
| long* a_current = ((long*) a) + i; | |
| long* b_current = ((long*) b) + i; | |
| if(*a_current < *b_current) { | |
| return -4 + i; | |
| } else if(*b_current < *a_current) { | |
| return 4 - i; | |
| } | |
| } | |
| // An empty string should be considered greater (and therefore more recent) than one with test | |
| if(a->extended[0] == '\0' && b->extended[0] != '\0') { | |
| return 1; | |
| } else if(b->extended[0] == '\0' && a->extended[0] != '\0') { | |
| return -1; | |
| } | |
| // Otherwise, just do a strncmp | |
| return strncmp(a->extended, b->extended, SDK_VERSION_BUFFER_SIZE); | |
| } | |
| int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions, size_t* count) | |
| { | |
| *install_paths = malloc(sizeof(char*) * 64); | |
| *install_versions = malloc(sizeof(struct SDKVersion) * 64); | |
| *count = 0; | |
| // We want the 64-bit registries on 64-bit windows, and the 32-bit registries on 32-bit Windows. | |
| // KEY_WOW64_64KEY accomplishes this because it gets ignored on 32-bit Windows. | |
| HKEY hKey; | |
| if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", | |
| 0, KEY_READ | KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS) { | |
| return 90; | |
| } | |
| size_t api_len = strlen(api_name); | |
| char* sdk_id = malloc(api_len + 4); | |
| strcpy(sdk_id, api_name); | |
| strcpy(sdk_id + api_len, "SDK"); | |
| DWORD keyCount, keyLen; | |
| RegQueryInfoKey(hKey, NULL, NULL, NULL, &keyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL); | |
| for(int i = 0; i < keyCount; ++i) { | |
| TCHAR name[COPY_BUFFER_SIZE]; | |
| DWORD nameSize = COPY_BUFFER_SIZE; | |
| RegEnumKeyEx(hKey, i, name, &nameSize, NULL, NULL, NULL, NULL); | |
| if(strncmp(sdk_id, name, api_len + 3)) { | |
| continue; | |
| } | |
| HKEY subKey; | |
| if(RegOpenKeyEx(hKey, name, 0, KEY_READ | KEY_WOW64_64KEY, &subKey) != ERROR_SUCCESS) { | |
| continue; | |
| } | |
| bool found_installation = false, found_version = false; | |
| DWORD valueCount; | |
| RegQueryInfoKey(subKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL); | |
| for(int j = 0; j < valueCount; ++j) { | |
| TCHAR name[COPY_BUFFER_SIZE], value[COPY_BUFFER_SIZE]; | |
| DWORD type, nameSize = COPY_BUFFER_SIZE, valSize = COPY_BUFFER_SIZE; | |
| LSTATUS ret = RegEnumValue(subKey, j, name, &nameSize, NULL, &type, value, &valSize); | |
| if(type == REG_SZ && !strcmp("InstallDir", name)) { | |
| *install_paths = realloc(*install_paths, sizeof(char*) * ((*count) + 1)); | |
| (*install_paths)[*count] = malloc(sizeof(char) * COPY_BUFFER_SIZE); | |
| strcpy((*install_paths)[*count], value); | |
| found_installation = true; | |
| } else if(type == REG_SZ && !strcmp("DisplayVersion", name)) { | |
| *install_versions = realloc(*install_versions, sizeof(struct SDKVersion) * ((*count) + 1)); | |
| CHECK_ERROR(read_version(value, (*install_versions) + *count)); | |
| found_version = true; | |
| } | |
| if(found_installation && found_version) { | |
| ++(*count); | |
| break; | |
| } | |
| } | |
| RegCloseKey(subKey); | |
| if(!(found_installation && found_version)) { | |
| RegCloseKey(hKey); | |
| free(sdk_id); | |
| return 100; | |
| } | |
| } | |
| RegCloseKey(hKey); | |
| free(sdk_id); | |
| return 0; | |
| } | |
| void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count) | |
| { | |
| for(size_t i = 0; i < count; ++i) { | |
| free(install_paths[i]); | |
| } | |
| free(install_paths); | |
| free(install_versions); | |
| } | |
| int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name) | |
| { | |
| *abi_major = 0; | |
| *api_name = "Vulkan"; | |
| // Parse arguments | |
| for(int i = 0; i < argc; ++i) { | |
| if(!strcmp(argv[i], FLAG_ABI_MAJOR)) { | |
| if(i + 1 == argc) { | |
| fprintf(log, "ERROR: No value given for flag %s.\n", FLAG_ABI_MAJOR); | |
| return 110; | |
| } | |
| *abi_major = strtol(argv[++i], NULL, 10); | |
| if(*abi_major == 0) { | |
| fprintf(log, "ERROR: Unable to parse ABI major version as integer.\n"); | |
| return 120; | |
| } | |
| } | |
| if(!strcmp(argv[i], FLAG_API_NAME)) { | |
| if(i + 1 == argc) { | |
| fprintf(log, "ERROR: No value given for flag %s.\n", FLAG_API_NAME); | |
| return 124; | |
| } | |
| *api_name = argv[++i]; | |
| } | |
| } | |
| // Check that we have everything we need | |
| if(*abi_major == 0 ) { | |
| fprintf(log, "ERROR: Flag %s must be provided.\n", FLAG_ABI_MAJOR); | |
| return 130; | |
| } | |
| // It all worked fine | |
| fprintf(log, "Found ABI: %ld\n\n", *abi_major); | |
| return 0; | |
| } | |
| int read_version(const char* version_string, struct SDKVersion* version) | |
| { | |
| size_t borders[4], dot_count = 0, i; | |
| for(i = 0; dot_count < 3 && version_string[i] != '\0'; ++i) { | |
| if(version_string[i] == '.') { | |
| borders[dot_count++] = i + 1; | |
| } | |
| } | |
| borders[3] = i + 1; | |
| if(dot_count < 3) { | |
| return 140; | |
| } | |
| // Read the version number | |
| version->major = strtol(version_string, NULL, 10); | |
| version->minor = strtol(version_string + borders[0], NULL, 10); | |
| version->patch = strtol(version_string + borders[1], NULL, 10); | |
| version->build = strtol(version_string + borders[2], NULL, 10); | |
| strncpy(version->extended, version_string + borders[3] + 1, | |
| min_s(SDK_VERSION_BUFFER_SIZE - 1, strlen(version_string + borders[3] + 1))); | |
| return 0; | |
| } | |
| int read_version_from_filename(const char* filename, struct SDKVersion* version) | |
| { | |
| size_t borders[5], dash_count = 0; | |
| // Locate all of the dashes that divides different version numbers | |
| size_t i; | |
| for(i = 0; dash_count < 5; ++i) { | |
| if(filename[i] == '-' && dash_count == 0) { | |
| ++dash_count; | |
| } else if(filename[i] == '-') { | |
| borders[dash_count++ - 1] = i + 1; | |
| } else if(filename[i] == '\0') { | |
| return 150; | |
| } | |
| } | |
| borders[4] = i + 1; | |
| // Read the version number | |
| version->major = strtol(filename + borders[0], NULL, 10); | |
| version->minor = strtol(filename + borders[1], NULL, 10); | |
| version->patch = strtol(filename + borders[2], NULL, 10); | |
| version->build = strtol(filename + borders[3], NULL, 10); | |
| if(strcmp(filename + borders[4] + 1, "dll") && strcmp(filename + borders[4] + 1, "exe")) { | |
| strncpy(version->extended, filename + borders[4] + 1, SDK_VERSION_BUFFER_SIZE - 1); | |
| size_t file_len = strlen(filename + borders[4] + 1); | |
| if(file_len - 4 < SDK_VERSION_BUFFER_SIZE) { | |
| version->extended[file_len - 4] = '\0'; | |
| } | |
| } else { | |
| version->extended[0] = '\0'; | |
| } | |
| for(size_t i = 0; version->extended[i] != '\0' && i < SDK_VERSION_BUFFER_SIZE; ++i) { | |
| if(version->extended[i] == '-') { | |
| version->extended[i] = '.'; | |
| } | |
| } | |
| return 0; | |
| } | |
| int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform, | |
| const char* api_name) | |
| { | |
| switch(platform) | |
| { | |
| case PLATFORM_X64: | |
| fprintf(log, "Removing x64 explicit layers from registry\n"); | |
| break; | |
| case PLATFORM_X86: | |
| fprintf(log, "Removing x86 explicit layers from registry\n"); | |
| break; | |
| } | |
| const char* pattern = "SOFTWARE\\Khronos\\%s\\ExplicitLayers"; | |
| int registry_size = snprintf(NULL, 0, pattern, api_name) + 1; | |
| char* registry_key = malloc(registry_size); | |
| snprintf(registry_key, registry_size, pattern, api_name); | |
| bool removed_one; | |
| do { | |
| // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries. | |
| // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node. | |
| HKEY hKey; | |
| REGSAM flags = KEY_ALL_ACCESS; | |
| if(platform == PLATFORM_X64) { | |
| flags |= KEY_WOW64_64KEY; | |
| } | |
| // Create (if needed) and open the explicit layer key | |
| if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags, | |
| NULL, &hKey, NULL) != ERROR_SUCCESS) { | |
| free(registry_key); | |
| return 160; | |
| } | |
| removed_one = false; | |
| DWORD valueCount; | |
| RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL); | |
| for(DWORD i = 0; i < valueCount; ++i) { | |
| TCHAR name[COPY_BUFFER_SIZE]; | |
| DWORD type, buffSize = COPY_BUFFER_SIZE; | |
| RegEnumValue(hKey, i, name, &buffSize, NULL, &type, NULL, NULL); | |
| for(size_t j = 0; j < count; ++j) { | |
| if(strncmp(install_paths[j], name, strlen(install_paths[j])) == 0) { | |
| fprintf(log, "Removing explicit layer entry: %s\n", name); | |
| LSTATUS err = RegDeleteValue(hKey, name); | |
| if(err != ERROR_SUCCESS) { | |
| free(registry_key); | |
| return 170; | |
| } | |
| removed_one = true; | |
| break; | |
| } | |
| } | |
| if(removed_one) { | |
| break; | |
| } | |
| } | |
| RegCloseKey(hKey); | |
| } while(removed_one); | |
| free(registry_key); | |
| return 0; | |
| } | |
| int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version, | |
| const char* api_name) | |
| { | |
| char** install_paths; | |
| struct SDKVersion* install_versions; | |
| size_t count; | |
| CHECK_ERROR_HANDLED(find_installations(api_name, &install_paths, &install_versions, &count), | |
| { free_installations(install_paths, install_versions, count); }); | |
| for(size_t i = 0; i < count; ++i) { | |
| fprintf(log, "Found installation of %ld.%ld.%ld.%ld in: %s\n", install_versions[i].major, | |
| install_versions[i].minor, install_versions[i].patch, install_versions[i].build, install_paths[i]); | |
| } | |
| fprintf(log, "\n"); | |
| if(platform == PLATFORM_X64) { | |
| CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X64, api_name), | |
| { free_installations(install_paths, install_versions, count); }); | |
| fprintf(log, "\n"); | |
| } | |
| CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X86, api_name), | |
| { free_installations(install_paths, install_versions, count); }); | |
| fprintf(log, "\n"); | |
| if(version->major == 0 && version->minor == 0 && version->patch == 0 && version->build == 0) { | |
| free_installations(install_paths, install_versions, count); | |
| return 0; | |
| } | |
| size_t preferred_sdk = count; | |
| for(size_t i = 0; i < count; ++i) { | |
| int cmp = compare_versions(install_versions + i, version); | |
| if(cmp <= 0 && cmp >= -2) { | |
| if(preferred_sdk == count || | |
| (compare_versions(install_versions + i, install_versions + preferred_sdk) > 0)) { | |
| preferred_sdk = i; | |
| } | |
| } | |
| } | |
| if(preferred_sdk < count) { | |
| if(platform == PLATFORM_X64) { | |
| CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[preferred_sdk], PLATFORM_X64, api_name), | |
| { free_installations(install_paths, install_versions, count); }); | |
| fprintf(log, "\n"); | |
| } | |
| CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[preferred_sdk], PLATFORM_X86, api_name), | |
| { free_installations(install_paths, install_versions, count); }); | |
| } | |
| free_installations(install_paths, install_versions, count); | |
| return 0; | |
| } | |
| //int update_system_file(FILE* log, const char* name, const char* extension, const char* path, | |
| // long abi_major, bool append_abi_major, struct SDKVersion* latest_version) | |
| int update_system_file(FILE* log, const char* name, const char* extension, const char* path, | |
| long abi_major, bool leave_abi_major, struct SDKVersion* latest_version) | |
| { | |
| // Generate the filter string | |
| const char* pattern = "%s%s-%ld-*-*-*-*%s"; | |
| int filter_size = snprintf(NULL, 0, pattern, path, name, abi_major, extension) + 1; | |
| if(filter_size < 0) { | |
| return 180; | |
| } | |
| char* filter = malloc(filter_size); | |
| snprintf(filter, filter_size, pattern, path, name, abi_major, extension); | |
| // Find all of the files that match the pattern | |
| char* latest_filename = malloc(64); | |
| memset(latest_version, 0, sizeof(struct SDKVersion)); | |
| WIN32_FIND_DATA find_data; | |
| HANDLE find = FindFirstFile(filter, &find_data); | |
| free(filter); | |
| for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end; | |
| at_end = FindNextFile(find, &find_data)) { | |
| struct SDKVersion version; | |
| CHECK_ERROR_HANDLED(read_version_from_filename(find_data.cFileName, &version), { free(latest_filename); }); | |
| // Decide if this is the latest file | |
| if(compare_versions(latest_version, &version) < 0) { | |
| *latest_version = version; | |
| const char* latestPattern = "%s%s"; | |
| int size = snprintf(NULL, 0, latestPattern, path, find_data.cFileName) + 1; | |
| if(size < 0) { | |
| free(latest_filename); | |
| return 200; | |
| } | |
| latest_filename = realloc(latest_filename, size); | |
| snprintf(latest_filename, size, latestPattern, path, find_data.cFileName); | |
| } | |
| } | |
| FindClose(find); | |
| // Make sure something was found | |
| if(latest_version->major == 0 && latest_version->minor == 0 && latest_version->patch == 0 && | |
| latest_version->build == 0) { | |
| fprintf(log, "Didn't find any version of %s%s\n", name, extension); | |
| return 0; | |
| } | |
| fprintf(log, "Found latest version of %s%s: %ld.%ld.%ld.%ld\n", name, extension, latest_version->major, | |
| latest_version->minor, latest_version->patch, latest_version->build); | |
| // Generate output filename | |
| char* output_filename; | |
| if(leave_abi_major) { | |
| const char* outPattern = "%s%s-%ld%s"; | |
| int out_size = snprintf(NULL, 0, outPattern, path, name, abi_major, extension) + 1; | |
| if(out_size < 0) { | |
| free(latest_filename); | |
| return 205; | |
| } | |
| output_filename = malloc(out_size); | |
| snprintf(output_filename, out_size, outPattern, path, name, abi_major, extension); | |
| } else { | |
| const char* outPattern = "%s%s%s"; | |
| int out_size = snprintf(NULL, 0, outPattern, path, name, extension) + 1; | |
| if(out_size < 0) { | |
| free(latest_filename); | |
| return 210; | |
| } | |
| output_filename = malloc(out_size); | |
| snprintf(output_filename, out_size, outPattern, path, name, extension); | |
| } | |
| // Remove any older version of the output file | |
| if(remove(output_filename) == 0) { | |
| fprintf(log, "Removed file %s\n", output_filename); | |
| } else { | |
| fprintf(log, "Did not remove file %s\n", output_filename); | |
| } | |
| fprintf(log, "Attempting to copy file %s to %s\n", latest_filename, output_filename); | |
| if(CopyFile(latest_filename, output_filename, false) == 0) { | |
| free(latest_filename); | |
| free(output_filename); | |
| return 215; | |
| } | |
| free(latest_filename); | |
| free(output_filename); | |
| return 0; | |
| } | |
| int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name, | |
| struct SDKVersion* latest_runtime_version) | |
| { | |
| size_t api_len = strlen(api_name); | |
| char* vulkan_name = malloc(api_len + 1); | |
| char* vulkan_info_name = malloc(api_len + 5); | |
| for(size_t i = 0; i < api_len; ++i) { | |
| vulkan_name[i] = tolower(api_name[i]); | |
| } | |
| vulkan_name[api_len] = '\0'; | |
| strcpy(vulkan_info_name, vulkan_name); | |
| strcpy(vulkan_info_name + api_len, "info"); | |
| struct SDKVersion version; | |
| unsigned windows_path_size = GetWindowsDirectory(NULL, 0); // Size includes null terminator | |
| char* system_path = malloc(windows_path_size + | |
| max_s(strlen(PATH_SYSTEM32), strlen(PATH_SYSWOW64))); | |
| GetWindowsDirectory(system_path, windows_path_size); | |
| strcpy(system_path + windows_path_size - 1, PATH_SYSTEM32); | |
| fprintf(log, "Updating system directory: %s\n", system_path); | |
| CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major, true, | |
| latest_runtime_version), { free(system_path); }); | |
| CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major, false, | |
| &version), { free(system_path); free(vulkan_info_name); free(vulkan_name);}); | |
| if(compare_versions(latest_runtime_version, &version) != 0) { | |
| free(system_path); | |
| free(vulkan_info_name); | |
| free(vulkan_name); | |
| return 220; | |
| } | |
| if(platform == PLATFORM_X64) { | |
| strcpy(system_path + windows_path_size - 1, PATH_SYSWOW64); | |
| fprintf(log, "\nUpdating system directory: %s\n", system_path); | |
| CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major, | |
| true, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); }); | |
| if(compare_versions(latest_runtime_version, &version) != 0) { | |
| free(system_path); | |
| free(vulkan_info_name); | |
| free(vulkan_name); | |
| return 230; | |
| } | |
| CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major, | |
| false, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); }); | |
| if(compare_versions(latest_runtime_version, &version) != 0) { | |
| free(system_path); | |
| free(vulkan_info_name); | |
| free(vulkan_name); | |
| return 240; | |
| } | |
| } | |
| free(system_path); | |
| free(vulkan_info_name); | |
| free(vulkan_name); | |
| fprintf(log, "\nUpdate of system directories succeeded.\n\n"); | |
| return 0; | |
| } |