Home | History | Annotate | Download | only in windowsRuntimeInstaller
      1 /******************************************************************************
      2  * Copyright (c) 2016 The Khronos Group
      3  * Copyright (c) 2016 Valve Corporation
      4  * Copyright (c) 2016 LunarG, Inc.
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you man not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *      https://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is destributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language govering permissions and
     16  * limitations under the License.
     17  *
     18  * Author: Lenny Komow <lenny (at) lunarg.com>
     19  *
     20  *****************************************************************************/
     21 
     22 /*
     23  * This program is used by the Vulkan Runtime Installer/Uninstaller to:
     24  * - Copy the most recent vulkan<majorabi>-*.dll in C:\Windows\System32
     25  *   to vulkan<majorabi>.dll
     26  * - Copy the most recent version of vulkaninfo-<abimajor>-*.exe in
     27  *   C:\Windows\System32 to vulkaninfo.exe
     28  * - The same thing is done for those files in C:\Windows\SysWOW64, but
     29  *   only on a 64-bit target
     30  * - Set the layer registry entried to point to the layer json files in
     31  *   the Vulkan SDK associated with the most recent vulkan*.dll
     32  *
     33  * The program must be called with the following parameters:
     34  *     --major-abi: A single number specifying the major abi version
     35  */
     36 
     37 // Compile with: `cl.exe configure_runtime.c /link advapi32.lib`
     38 // Be sure to use the x86 version of cl.exe
     39 
     40 #include <stdbool.h>
     41 #include <stdint.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <windows.h>
     46 
     47 // This hack gets Visual Studio 2013 to handle C99 stuff properly
     48 // If we drop support for 2013, it would be a good idea to remove this
     49 #if _MSC_VER < 1900
     50 #define inline __inline
     51 #define snprintf _snprintf
     52 #endif
     53 
     54 #if defined(_WIN64)
     55 #error "This program is designed only as a 32-bit program. It should not be built as 64-bit."
     56 #endif
     57 
     58 #define COPY_BUFFER_SIZE (1024)
     59 #define CHECK_ERROR(statement) { int error = (statement); if(error) return error; }
     60 #define CHECK_ERROR_HANDLED(statement, handler) { int error = (statement); if(error) { { handler } return error; } }
     61 #define SDK_VERSION_BUFFER_SIZE (64)
     62 
     63 enum Platform
     64 {
     65     PLATFORM_X64,
     66     PLATFORM_X86,
     67 };
     68 
     69 #pragma pack(1)
     70 struct SDKVersion
     71 {
     72     long major;
     73     long minor;
     74     long patch;
     75     long build;
     76     char extended[SDK_VERSION_BUFFER_SIZE];
     77 };
     78 
     79 const char* FLAG_ABI_MAJOR = "--abi-major";
     80 const char* PATH_SYSTEM32 = "\\SYSTEM32\\";
     81 const char* PATH_SYSWOW64 = "\\SysWOW64\\";
     82 
     83 inline size_t max_s(size_t a, size_t b) { return a > b ? a : b; }
     84 inline size_t min_s(size_t a, size_t b) { return a > b ? a : b; }
     85 
     86 // Add the registry entries for all explicit layers
     87 //
     88 // log (input) - Logging file stream
     89 // install_path (input) - The installation path of the SDK which provides the layers
     90 // platform (input) - The platform to set the installation for (x64 or x86)
     91 // Returns: Zero on success, an error code on failure
     92 int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform);
     93 
     94 // Compare two sdk versions
     95 //
     96 // Returns: Zero if they are equal, below zero if a predates b, greater than zero if b predates a
     97 int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b);
     98 
     99 // Locate all of the SDK installations
    100 //
    101 // install_paths (output) - A poiner to an array of the installations paths
    102 // install_versions (output) - A pointer to an array of the SDK versions
    103 // count (output) - A pointer to the number of items in each array
    104 // Returns: Zero on success, an error code on failure
    105 //
    106 // Both install_paths and install_versions are allocated on the heap. To free them properly,
    107 // call free_installations(), even if this function returned an error code. The orders of
    108 // install_paths and install_versions match, so (*install_paths)[2] is guaranteed to match
    109 // (*install_versions)[2]
    110 int find_installations(char*** install_paths, struct SDKVersion** install_versions, size_t* count);
    111 
    112 // Free the memory allocated by find_installations()
    113 void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count);
    114 
    115 // Parse command line arguments for the program
    116 //
    117 // log (input) - Logging file stream
    118 // argc (input) - The argument count
    119 // argv (input) - An array of argument strings
    120 // abi_major (output) - The major abi version from the arguments
    121 // Returns: Zero on success, an error code on failure
    122 int parse_arguments(FILE* log, int argc, char** argv, long* abi_major);
    123 
    124 // Read the version from a string
    125 //
    126 // version_string (input) - A string in the format <abi>.<major>.<minor>.<patch>.<build>.<extended>
    127 // version (output) - The version indicated by the input string
    128 // Returns: Zero on success, an error code on failure
    129 int read_version(const char* version_string, struct SDKVersion* version);
    130 
    131 // Read the version from a filename
    132 //
    133 // filename (input) - The name of a .dll or .exe file, in the format
    134 //     somename-<abi>-<major>-<minor>-<path>-<build>-<extended>.dll
    135 // version (output) - The versions indicated by the input string
    136 // Returns: Zero on success, an error code on failure
    137 int read_version_from_filename(const char* filename, struct SDKVersion* version);
    138 
    139 // Remove explicit layers from the Windows registry
    140 //
    141 // log (input) - Loggin file stream
    142 // install_paths (input) - An array of every vulkan installation path
    143 // count (input) - The number of vulkan installations
    144 // platform (input) - The platform (x64 or x86) of the registry to use (both exist on x64)
    145 // Returns: Zero on success, an error code on failure
    146 int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform);
    147 
    148 // Update all explicity layers in the windows registry
    149 //
    150 // log (input) - Logging file stream
    151 // platform (input) - The platform of the OS (both registries will be modified if this is x64)
    152 // version (input) - The version that should be set to current (if it exists)
    153 // Returns: Zero on success, an error code on failure
    154 int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version);
    155 
    156 // Update a single vulkan system file (vulkan.dll or vulkaninfo.exe)
    157 //
    158 // log (input) - Loggin file stream
    159 // name (input) - The name (excuding file extension) of the file to be updated
    160 // extension (input) - The file extensions of the file to be updated
    161 // path (input) - The directory of the file (usually System32 or SysWOW64)
    162 // abi_major (input) - The ABI major version to be updated
    163 // append_abi_major (input) - Whether or not the ABI number should be appended to the filename
    164 // latest_version (output) - The version of the runtime which the file was updated to
    165 // Returns: Zero on success, an error code on failure
    166 int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
    167     long abi_major, bool append_abi_major, struct SDKVersion* latest_version);
    168 
    169 // Update vulkan.dll and vulkaninfo.exe in all of the windows directories (System32 and SysWOW64)
    170 //
    171 // log (input) - Loging file stream
    172 // abi_major (input) - The ABI major version of the files that should be used
    173 // platform (input) - The platform for the current OS
    174 // latest_runtime_version (output) - The version that the runtime files were updated to
    175 int update_windows_directories(FILE* log, long abi_major, enum Platform platform,
    176     struct SDKVersion* latest_runtime_version);
    177 
    178 int main(int argc, char** argv)
    179 {
    180     // Get the OS platform (x86 or x64)
    181     BOOL is_64_bit;
    182     IsWow64Process(GetCurrentProcess(), &is_64_bit);
    183     enum Platform platform = is_64_bit ? PLATFORM_X64 : PLATFORM_X86;
    184 
    185     FILE* log = fopen("configure_rt.log", "w");
    186     if(log == NULL) {
    187         return 10;
    188     }
    189 
    190     // Parse the arguments to get the abi version and the number of bits of the OS
    191     long abi_major;
    192     CHECK_ERROR_HANDLED(parse_arguments(log, argc, argv, &abi_major), { fclose(log); });
    193 
    194     // This makes System32 and SysWOW64 not do any redirection (well, until 128-bit is a thing)
    195     Wow64DisableWow64FsRedirection(NULL);
    196 
    197     // Update System32 (on all systems) and SysWOW64 on 64-bit system
    198     struct SDKVersion latest_runtime_version;
    199     CHECK_ERROR_HANDLED(update_windows_directories(log, abi_major, platform, &latest_runtime_version),
    200         { fclose(log); });
    201 
    202     // Update the explicit layers that are set in the windows registry
    203     CHECK_ERROR_HANDLED(update_registry_layers(log, platform, &latest_runtime_version), { fclose(log); });
    204 
    205     fclose(log);
    206     return 0;
    207 }
    208 
    209 int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform)
    210 {
    211     switch(platform)
    212     {
    213     case PLATFORM_X64:
    214         fprintf(log, "Updating x64 explicit layers to path: %s\n", install_path);
    215         break;
    216     case PLATFORM_X86:
    217         fprintf(log, "Updating x86 explicit layers to path: %s\n", install_path);
    218         break;
    219     }
    220 
    221     // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries.
    222     // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node.
    223     HKEY hKey;
    224     REGSAM flags = KEY_ALL_ACCESS;
    225     if(platform == PLATFORM_X64) {
    226         flags |= KEY_WOW64_64KEY;
    227     }
    228 
    229     // Create (if needed) and open the explicit layer key
    230     if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Khronos\\Vulkan\\ExplicitLayers",
    231         0, NULL, REG_OPTION_NON_VOLATILE, flags, NULL, &hKey, NULL) != ERROR_SUCCESS) {
    232         return 20;
    233     }
    234 
    235     const char* pattern = platform == PLATFORM_X64 ? "%s\\Bin\\VkLayer*.json" : "%s\\Bin32\\VkLayer*.json";
    236     int filter_size = snprintf(NULL, 0, pattern, install_path) + 1;
    237     if(filter_size < 0) {
    238         return 30;
    239     }
    240     char* filter = malloc(filter_size);
    241     snprintf(filter, filter_size, pattern, install_path);
    242 
    243     WIN32_FIND_DATA find_data;
    244     HANDLE find = FindFirstFile(filter, &find_data);
    245     free(filter);
    246     for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end;
    247         at_end = FindNextFile(find, &find_data)) {
    248 
    249         const char* layer_pattern = platform == PLATFORM_X64 ? "%s\\Bin\\%s" : "%s\\Bin32\\%s";
    250         int layer_size = snprintf(NULL, 0, layer_pattern, install_path, find_data.cFileName) + 1;
    251         if(layer_size < 0) {
    252             return 40;
    253         }
    254         char* layer = malloc(layer_size);
    255         snprintf(layer, layer_size, layer_pattern, install_path, find_data.cFileName);
    256 
    257         fprintf(log, "Adding explicit layer: %s\n", layer);
    258 
    259         DWORD zero = 0;
    260         LSTATUS err = RegSetValueEx(hKey, layer, zero, REG_DWORD, (BYTE*) &zero, sizeof(DWORD));
    261         free(layer);
    262         if(err != ERROR_SUCCESS) {
    263             return 50;
    264         }
    265     }
    266 
    267     RegCloseKey(hKey);
    268     return 0;
    269 }
    270 
    271 int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b)
    272 {
    273     // Compare numerical versions
    274     for(int i = 0; i < 4; ++i) {
    275         long* a_current = ((long*) a) + i;
    276         long* b_current = ((long*) b) + i;
    277 
    278         if(*a_current < *b_current) {
    279             return -4 + i;
    280         } else if(*b_current < *a_current) {
    281             return 4 - i;
    282         }
    283     }
    284 
    285     // An empty string should be considered greater (and therefore more recent) than one with test
    286     if(a->extended[0] == '\0' && b->extended[0] != '\0') {
    287         return 1;
    288     } else if(b->extended[0] == '\0' && a->extended[0] != '\0') {
    289         return -1;
    290     }
    291 
    292     // Otherwise, just do a strncmp
    293     return strncmp(a->extended, b->extended, SDK_VERSION_BUFFER_SIZE);
    294 }
    295 
    296 int find_installations(char*** install_paths, struct SDKVersion** install_versions, size_t* count)
    297 {
    298     *install_paths = malloc(sizeof(char*) * 64);
    299     *install_versions = malloc(sizeof(struct SDKVersion) * 64);
    300     *count = 0;
    301 
    302     // We want the 64-bit registries on 64-bit windows, and the 32-bit registries on 32-bit Windows.
    303     // KEY_WOW64_64KEY accomplishes this because it gets ignored on 32-bit Windows.
    304     HKEY hKey;
    305     if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
    306         0, KEY_READ | KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS) {
    307         return 90;
    308     }
    309 
    310     DWORD keyCount, keyLen;
    311     RegQueryInfoKey(hKey, NULL, NULL, NULL, &keyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    312     for(int i = 0; i < keyCount; ++i) {
    313         TCHAR name[COPY_BUFFER_SIZE];
    314         DWORD nameSize = COPY_BUFFER_SIZE;
    315         RegEnumKeyEx(hKey, i, name, &nameSize, NULL, NULL, NULL, NULL);
    316 
    317         if(strncmp("VulkanSDK", name, 9)) {
    318             continue;
    319         }
    320 
    321         HKEY subKey;
    322         if(RegOpenKeyEx(hKey, name, 0, KEY_READ | KEY_WOW64_64KEY, &subKey) != ERROR_SUCCESS) {
    323             continue;
    324         }
    325 
    326         bool found_installation = false, found_version = false;
    327         DWORD valueCount;
    328         RegQueryInfoKey(subKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL);
    329         for(int j = 0; j < valueCount; ++j) {
    330 
    331             TCHAR name[COPY_BUFFER_SIZE], value[COPY_BUFFER_SIZE];
    332             DWORD type, buffSize = COPY_BUFFER_SIZE;
    333             RegEnumValue(subKey, j, name, &buffSize, NULL, &type, value, &buffSize);
    334             if(type == REG_SZ && !strcmp("InstallDir", name)) {
    335                 *install_paths = realloc(*install_paths, sizeof(char*) * ((*count) + 1));
    336                 (*install_paths)[*count] = malloc(sizeof(char) * COPY_BUFFER_SIZE);
    337                 strcpy((*install_paths)[*count], value);
    338                 found_installation = true;
    339             } else if(type == REG_SZ && !strncmp("DisplayVersion", name, 8)) {
    340                 *install_versions = realloc(*install_versions, sizeof(struct SDKVersion) * ((*count) + 1));
    341                 CHECK_ERROR(read_version(value, (*install_versions) + *count));
    342                 found_version = true;
    343             }
    344 
    345             if(found_installation && found_version) {
    346                 ++(*count);
    347                 break;
    348             }
    349         }
    350         RegCloseKey(subKey);
    351 
    352         if(!(found_installation && found_version)) {
    353             RegCloseKey(hKey);
    354             return 100;
    355         }
    356     }
    357     RegCloseKey(hKey);
    358 
    359     return 0;
    360 }
    361 
    362 void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count)
    363 {
    364     for(size_t i = 0; i < count; ++i) {
    365         free(install_paths[i]);
    366     }
    367     free(install_paths);
    368     free(install_versions);
    369 }
    370 
    371 int parse_arguments(FILE* log, int argc, char** argv, long* abi_major)
    372 {
    373     *abi_major = 0;
    374 
    375     // Parse arguments
    376     for(int i = 0; i < argc; ++i) {
    377         if(!strcmp(argv[i], FLAG_ABI_MAJOR)) {
    378             if(i + 1 == argc) {
    379                 fprintf(log, "ERROR: No value given for flag %s.\n", FLAG_ABI_MAJOR);
    380                 return 110;
    381             }
    382             *abi_major = strtol(argv[++i], NULL, 10);
    383             if(*abi_major == 0) {
    384                 fprintf(log, "ERROR: Unable to parse ABI major version as integer.\n");
    385                 return 120;
    386             }
    387         }
    388     }
    389 
    390     // Check that we have everything we need
    391     if(*abi_major == 0 ) {
    392         fprintf(log, "ERROR: Flag %s must be provided.\n", FLAG_ABI_MAJOR);
    393         return 130;
    394     }
    395 
    396     // It all worked fine
    397     fprintf(log, "Found ABI: %ld\n\n", *abi_major);
    398     return 0;
    399 }
    400 
    401 int read_version(const char* version_string, struct SDKVersion* version)
    402 {
    403     size_t borders[4], dot_count = 0, i;
    404     for(i = 0; dot_count < 3 && version_string[i] != '\0'; ++i) {
    405         if(version_string[i] == '.') {
    406             borders[dot_count++] = i + 1;
    407         }
    408     }
    409     borders[3] = i + 1;
    410 
    411     if(dot_count < 3) {
    412         return 140;
    413     }
    414 
    415     // Read the version number
    416     version->major = strtol(version_string,              NULL, 10);
    417     version->minor = strtol(version_string + borders[0], NULL, 10);
    418     version->patch = strtol(version_string + borders[1], NULL, 10);
    419     version->build = strtol(version_string + borders[2], NULL, 10);
    420 
    421     strncpy(version->extended, version_string + borders[3] + 1,
    422         min_s(SDK_VERSION_BUFFER_SIZE - 1, strlen(version_string + borders[3] + 1)));
    423 
    424     return 0;
    425 }
    426 
    427 int read_version_from_filename(const char* filename, struct SDKVersion* version)
    428 {
    429     size_t borders[5], dash_count = 0;
    430 
    431     // Locate all of the dashes that divides different version numbers
    432     size_t i;
    433     for(i = 0; dash_count < 5; ++i) {
    434         if(filename[i] == '-' && dash_count == 0) {
    435             ++dash_count;
    436         } else if(filename[i] == '-') {
    437             borders[dash_count++ - 1] = i + 1;
    438         } else if(filename[i] == '\0') {
    439             return 150;
    440         }
    441     }
    442     borders[4] = i + 1;
    443 
    444     // Read the version number
    445     version->major = strtol(filename + borders[0], NULL, 10);
    446     version->minor = strtol(filename + borders[1], NULL, 10);
    447     version->patch = strtol(filename + borders[2], NULL, 10);
    448     version->build = strtol(filename + borders[3], NULL, 10);
    449 
    450     if(strcmp(filename + borders[4] + 1, "dll") && strcmp(filename + borders[4] + 1, "exe")) {
    451         strncpy(version->extended, filename + borders[4] + 1, SDK_VERSION_BUFFER_SIZE - 1);
    452         size_t file_len = strlen(filename + borders[4] + 1);
    453         if(file_len - 4 < SDK_VERSION_BUFFER_SIZE) {
    454             version->extended[file_len - 4] = '\0';
    455         }
    456     } else {
    457         version->extended[0] = '\0';
    458     }
    459 
    460     for(size_t i = 0; version->extended[i] != '\0' && i < SDK_VERSION_BUFFER_SIZE; ++i) {
    461         if(version->extended[i] == '-') {
    462             version->extended[i] = '.';
    463         }
    464     }
    465 
    466     return 0;
    467 }
    468 
    469 int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform)
    470 {
    471     switch(platform)
    472     {
    473     case PLATFORM_X64:
    474         fprintf(log, "Removing x64 explicit layers from registry\n");
    475         break;
    476     case PLATFORM_X86:
    477         fprintf(log, "Removing x86 explicit layers from registry\n");
    478         break;
    479     }
    480 
    481     bool removed_one;
    482     do {
    483         // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries.
    484         // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node.
    485         HKEY hKey;
    486         REGSAM flags = KEY_ALL_ACCESS;
    487         if(platform == PLATFORM_X64) {
    488             flags |= KEY_WOW64_64KEY;
    489         }
    490 
    491         // Create (if needed) and open the explicit layer key
    492         if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Khronos\\Vulkan\\ExplicitLayers",
    493             0, NULL, REG_OPTION_NON_VOLATILE, flags, NULL, &hKey, NULL) != ERROR_SUCCESS) {
    494             return 160;
    495         }
    496 
    497         removed_one = false;
    498         DWORD valueCount;
    499         RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL);
    500         for(DWORD i = 0; i < valueCount; ++i) {
    501             TCHAR name[COPY_BUFFER_SIZE];
    502             DWORD type, buffSize = COPY_BUFFER_SIZE;
    503             RegEnumValue(hKey, i, name, &buffSize, NULL, &type, NULL, NULL);
    504 
    505             for(size_t j = 0; j < count; ++j) {
    506                 if(strncmp(install_paths[j], name, strlen(install_paths[j])) == 0) {
    507                     fprintf(log, "Removing explicit layer entry: %s\n", name);
    508                     LSTATUS err = RegDeleteValue(hKey, name);
    509                     if(err != ERROR_SUCCESS) {
    510                         return 170;
    511                     }
    512                     removed_one = true;
    513                     break;
    514                 }
    515             }
    516             if(removed_one) {
    517                 break;
    518             }
    519         }
    520 
    521         RegCloseKey(hKey);
    522     } while(removed_one);
    523 
    524     return 0;
    525 }
    526 
    527 int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version)
    528 {
    529     char** install_paths;
    530     struct SDKVersion* install_versions;
    531     size_t count;
    532     CHECK_ERROR_HANDLED(find_installations(&install_paths, &install_versions, &count),
    533         { free_installations(install_paths, install_versions, count); });
    534     for(size_t i = 0; i < count; ++i) {
    535         fprintf(log, "Found installation of %ld.%ld.%ld.%ld in: %s\n", install_versions[i].major,
    536             install_versions[i].minor, install_versions[i].patch, install_versions[i].build, install_paths[i]);
    537     }
    538     fprintf(log, "\n");
    539     if(platform == PLATFORM_X64) {
    540         CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X64),
    541             { free_installations(install_paths, install_versions, count); });
    542         fprintf(log, "\n");
    543     }
    544     CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X86),
    545         { free_installations(install_paths, install_versions, count); });
    546     fprintf(log, "\n");
    547 
    548     if(version->major == 0 && version->minor == 0 && version->patch == 0 && version->build == 0) {
    549         free_installations(install_paths, install_versions, count);
    550         return 0;
    551     }
    552 
    553     for(size_t i = 0; i < count; ++i) {
    554         if(compare_versions(install_versions + i, version) == 0) {
    555             if(platform == PLATFORM_X64) {
    556                 CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[i], PLATFORM_X64),
    557                     { free_installations(install_paths, install_versions, count); });
    558                 fprintf(log, "\n");
    559             }
    560             CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[i], PLATFORM_X86),
    561                 { free_installations(install_paths, install_versions, count); });
    562             break;
    563         }
    564     }
    565     free_installations(install_paths, install_versions, count);
    566     return 0;
    567 }
    568 
    569 //int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
    570 //    long abi_major, bool append_abi_major, struct SDKVersion* latest_version)
    571 int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
    572     long abi_major, bool leave_abi_major, struct SDKVersion* latest_version)
    573 {
    574     // Generate the filter string
    575     const char* pattern = "%s%s-%ld-*-*-*-*%s";
    576     int filter_size = snprintf(NULL, 0, pattern, path, name, abi_major, extension) + 1;
    577     if(filter_size < 0) {
    578         return 180;
    579     }
    580     char* filter = malloc(filter_size);
    581     snprintf(filter, filter_size, pattern, path, name, abi_major, extension);
    582 
    583     // Find all of the files that match the pattern
    584     char* latest_filename = malloc(64);
    585     memset(latest_version, 0, sizeof(struct SDKVersion));
    586     WIN32_FIND_DATA find_data;
    587     HANDLE find = FindFirstFile(filter, &find_data);
    588     free(filter);
    589     for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end;
    590         at_end = FindNextFile(find, &find_data)) {
    591 
    592         struct SDKVersion version;
    593         CHECK_ERROR_HANDLED(read_version_from_filename(find_data.cFileName, &version), { free(latest_filename); });
    594 
    595         // Decide if this is the latest file
    596         if(compare_versions(latest_version, &version) < 0) {
    597             *latest_version = version;
    598             const char* latestPattern = "%s%s";
    599             int size = snprintf(NULL, 0, latestPattern, path, find_data.cFileName) + 1;
    600             if(size < 0) {
    601                 free(latest_filename);
    602                 return 200;
    603             }
    604             latest_filename = realloc(latest_filename, size);
    605             snprintf(latest_filename, size, latestPattern, path, find_data.cFileName);
    606         }
    607     }
    608     FindClose(find);
    609 
    610     // Make sure something was found
    611     if(latest_version->major == 0 && latest_version->minor == 0 && latest_version->patch == 0 &&
    612         latest_version->build == 0) {
    613         fprintf(log, "Didn't find any version of %s%s\n", name, extension);
    614         return 0;
    615     }
    616 
    617     fprintf(log, "Found latest version of %s%s: %ld.%ld.%ld.%ld\n", name, extension, latest_version->major,
    618         latest_version->minor, latest_version->patch, latest_version->build);
    619 
    620     // Generate output filename
    621     char* output_filename;
    622     if(leave_abi_major) {
    623         const char* outPattern = "%s%s-%ld%s";
    624         int out_size = snprintf(NULL, 0, outPattern, path, name, abi_major, extension) + 1;
    625         if(out_size < 0) {
    626             free(latest_filename);
    627             return 205;
    628         }
    629         output_filename = malloc(out_size);
    630         snprintf(output_filename, out_size, outPattern, path, name, abi_major, extension);
    631     } else {
    632         const char* outPattern = "%s%s%s";
    633         int out_size = snprintf(NULL, 0, outPattern, path, name, extension) + 1;
    634         if(out_size < 0) {
    635             free(latest_filename);
    636             return 210;
    637         }
    638         output_filename = malloc(out_size);
    639         snprintf(output_filename, out_size, outPattern, path, name, extension);
    640     }
    641 
    642     // Remove any older version of the output file
    643     if(remove(output_filename) == 0) {
    644         fprintf(log, "Removed file %s\n", output_filename);
    645     } else {
    646         fprintf(log, "Did not remove file %s\n", output_filename);
    647     }
    648 
    649     fprintf(log, "Attempting to copy file %s to %s\n", latest_filename, output_filename);
    650     if(CopyFile(latest_filename, output_filename, false) == 0) {
    651         free(latest_filename);
    652         free(output_filename);
    653         return 215;
    654     }
    655 
    656     free(latest_filename);
    657     free(output_filename);
    658     return 0;
    659 }
    660 
    661 int update_windows_directories(FILE* log, long abi_major, enum Platform platform, struct SDKVersion* latest_runtime_version)
    662 {
    663     struct SDKVersion version;
    664     unsigned windows_path_size = GetWindowsDirectory(NULL, 0); // Size includes null terminator
    665     char* system_path = malloc(windows_path_size +
    666         max_s(strlen(PATH_SYSTEM32), strlen(PATH_SYSWOW64)));
    667     GetWindowsDirectory(system_path, windows_path_size);
    668 
    669     strcpy(system_path + windows_path_size - 1, PATH_SYSTEM32);
    670     fprintf(log, "Updating system directory: %s\n", system_path);
    671     CHECK_ERROR_HANDLED(update_system_file(log, "vulkan", ".dll", system_path, abi_major, true,
    672         latest_runtime_version), { free(system_path); });
    673     CHECK_ERROR_HANDLED(update_system_file(log, "vulkaninfo", ".exe", system_path, abi_major, false,
    674         &version), { free(system_path); });
    675     if(compare_versions(latest_runtime_version, &version) != 0) {
    676         free(system_path);
    677         return 220;
    678     }
    679 
    680     if(platform == PLATFORM_X64) {
    681         strcpy(system_path + windows_path_size - 1, PATH_SYSWOW64);
    682         fprintf(log, "\nUpdating system directory: %s\n", system_path);
    683         CHECK_ERROR_HANDLED(update_system_file(log, "vulkan", ".dll", system_path, abi_major,
    684             true, &version), { free(system_path); });
    685         if(compare_versions(latest_runtime_version, &version) != 0) {
    686             free(system_path);
    687             return 230;
    688         }
    689         CHECK_ERROR_HANDLED(update_system_file(log, "vulkaninfo", ".exe", system_path, abi_major,
    690             false, &version), { free(system_path); });
    691         if(compare_versions(latest_runtime_version, &version) != 0) {
    692             free(system_path);
    693             return 240;
    694         }
    695     }
    696 
    697     free(system_path);
    698     fprintf(log, "\nUpdate of system directories succeeded.\n\n");
    699     return 0;
    700 }
    701