• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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* FLAG_API_NAME = "--api-name";
81 const char* PATH_SYSTEM32 = "\\SYSTEM32\\";
82 const char* PATH_SYSWOW64 = "\\SysWOW64\\";
83 
max_s(size_t a,size_t b)84 inline size_t max_s(size_t a, size_t b) { return a > b ? a : b; }
min_s(size_t a,size_t b)85 inline size_t min_s(size_t a, size_t b) { return a > b ? a : b; }
86 
87 // Add the registry entries for all explicit layers
88 //
89 // log (input) - Logging file stream
90 // install_path (input) - The installation path of the SDK which provides the layers
91 // platform (input) - The platform to set the installation for (x64 or x86)
92 // api_name (input) - The api name to use when working with registries
93 // Returns: Zero on success, an error code on failure
94 int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name);
95 
96 // Compare two sdk versions
97 //
98 // Returns: Zero if they are equal, below zero if a predates b, greater than zero if b predates a
99 int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b);
100 
101 // Locate all of the SDK installations
102 //
103 // api_name (input) - The api name to use when working with registries
104 // install_paths (output) - A poiner to an array of the installations paths
105 // install_versions (output) - A pointer to an array of the SDK versions
106 // count (output) - A pointer to the number of items in each array
107 // Returns: Zero on success, an error code on failure
108 //
109 // Both install_paths and install_versions are allocated on the heap. To free them properly,
110 // call free_installations(), even if this function returned an error code. The orders of
111 // install_paths and install_versions match, so (*install_paths)[2] is guaranteed to match
112 // (*install_versions)[2]
113 int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions,
114     size_t* count);
115 
116 // Free the memory allocated by find_installations()
117 void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count);
118 
119 // Parse command line arguments for the program
120 //
121 // log (input) - Logging file stream
122 // argc (input) - The argument count
123 // argv (input) - An array of argument strings
124 // abi_major (output) - The major abi version from the arguments
125 // api_name (output) - The api name to use when working with registries and system files
126 // Returns: Zero on success, an error code on failure
127 int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name);
128 
129 // Read the version from a string
130 //
131 // version_string (input) - A string in the format <abi>.<major>.<minor>.<patch>.<build>.<extended>
132 // version (output) - The version indicated by the input string
133 // Returns: Zero on success, an error code on failure
134 int read_version(const char* version_string, struct SDKVersion* version);
135 
136 // Read the version from a filename
137 //
138 // filename (input) - The name of a .dll or .exe file, in the format
139 //     somename-<abi>-<major>-<minor>-<path>-<build>-<extended>.dll
140 // version (output) - The versions indicated by the input string
141 // Returns: Zero on success, an error code on failure
142 int read_version_from_filename(const char* filename, struct SDKVersion* version);
143 
144 // Remove explicit layers from the Windows registry
145 //
146 // log (input) - Loggin file stream
147 // install_paths (input) - An array of every vulkan installation path
148 // count (input) - The number of vulkan installations
149 // platform (input) - The platform (x64 or x86) of the registry to use (both exist on x64)
150 // api_name (input) - The api name to use when working with registries
151 // Returns: Zero on success, an error code on failure
152 int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform,
153     const char* api_name);
154 
155 // Update all explicity layers in the windows registry
156 //
157 // log (input) - Logging file stream
158 // platform (input) - The platform of the OS (both registries will be modified if this is x64)
159 // version (input) - The version that should be set to current (if it exists)
160 // api_name (input) - The api name to use when working with registries
161 // Returns: Zero on success, an error code on failure
162 int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version,
163     const char* api_name);
164 
165 // Update a single vulkan system file (vulkan.dll or vulkaninfo.exe)
166 //
167 // log (input) - Loggin file stream
168 // name (input) - The name (excuding file extension) of the file to be updated
169 // extension (input) - The file extensions of the file to be updated
170 // path (input) - The directory of the file (usually System32 or SysWOW64)
171 // abi_major (input) - The ABI major version to be updated
172 // append_abi_major (input) - Whether or not the ABI number should be appended to the filename
173 // latest_version (output) - The version of the runtime which the file was updated to
174 // Returns: Zero on success, an error code on failure
175 int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
176     long abi_major, bool append_abi_major, struct SDKVersion* latest_version);
177 
178 // Update vulkan.dll and vulkaninfo.exe in all of the windows directories (System32 and SysWOW64)
179 //
180 // log (input) - Loging file stream
181 // abi_major (input) - The ABI major version of the files that should be used
182 // platform (input) - The platform for the current OS
183 // api_name (input) - The api name to use when working with system files
184 // latest_runtime_version (output) - The version that the runtime files were updated to
185 int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name,
186     struct SDKVersion* latest_runtime_version);
187 
main(int argc,char ** argv)188 int main(int argc, char** argv)
189 {
190     // Get the OS platform (x86 or x64)
191     BOOL is_64_bit;
192     IsWow64Process(GetCurrentProcess(), &is_64_bit);
193     enum Platform platform = is_64_bit ? PLATFORM_X64 : PLATFORM_X86;
194 
195     FILE* log = fopen("configure_rt.log", "w");
196     if(log == NULL) {
197         return 10;
198     }
199 
200     // Parse the arguments to get the abi version and the number of bits of the OS
201     long abi_major;
202     const char* api_name;
203     CHECK_ERROR_HANDLED(parse_arguments(log, argc, argv, &abi_major, &api_name), { fclose(log); });
204 
205     fprintf(log, "API Name: %s\n", api_name);
206 
207     // This makes System32 and SysWOW64 not do any redirection (well, until 128-bit is a thing)
208     PVOID useless;
209     Wow64DisableWow64FsRedirection(&useless);
210 
211     // Update System32 (on all systems) and SysWOW64 on 64-bit system
212     struct SDKVersion latest_runtime_version;
213     CHECK_ERROR_HANDLED(update_windows_directories(log, abi_major, platform, api_name,
214         &latest_runtime_version), { fclose(log); });
215 
216     // Update the explicit layers that are set in the windows registry
217     CHECK_ERROR_HANDLED(update_registry_layers(log, platform, &latest_runtime_version, api_name),
218         { fclose(log); });
219 
220     fclose(log);
221     return 0;
222 }
223 
add_explicit_layers(FILE * log,const char * install_path,enum Platform platform,const char * api_name)224 int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name)
225 {
226     switch(platform)
227     {
228     case PLATFORM_X64:
229         fprintf(log, "Updating x64 explicit layers to path: %s\n", install_path);
230         break;
231     case PLATFORM_X86:
232         fprintf(log, "Updating x86 explicit layers to path: %s\n", install_path);
233         break;
234     }
235 
236     const char* registry_pattern = "SOFTWARE\\Khronos\\%s\\ExplicitLayers";
237     int registry_size = snprintf(NULL, 0, registry_pattern, api_name) + 1;
238     char* registry_key = malloc(registry_size);
239     snprintf(registry_key, registry_size, registry_pattern, api_name);
240 
241     // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries.
242     // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node.
243     HKEY hKey;
244     REGSAM flags = KEY_ALL_ACCESS;
245     if(platform == PLATFORM_X64) {
246         flags |= KEY_WOW64_64KEY;
247     }
248 
249     // Create (if needed) and open the explicit layer key
250     if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags,
251          NULL, &hKey, NULL) != ERROR_SUCCESS) {
252         free(registry_key);
253         return 20;
254     }
255 
256     const char* pattern = platform == PLATFORM_X64 ? "%s\\Bin\\VkLayer*.json" : "%s\\Bin32\\VkLayer*.json";
257     int filter_size = snprintf(NULL, 0, pattern, install_path) + 1;
258     if(filter_size < 0) {
259         return 30;
260     }
261     char* filter = malloc(filter_size);
262     snprintf(filter, filter_size, pattern, install_path);
263 
264     WIN32_FIND_DATA find_data;
265     HANDLE find = FindFirstFile(filter, &find_data);
266     free(filter);
267     for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end;
268         at_end = FindNextFile(find, &find_data)) {
269 
270         const char* layer_pattern = platform == PLATFORM_X64 ? "%s\\Bin\\%s" : "%s\\Bin32\\%s";
271         int layer_size = snprintf(NULL, 0, layer_pattern, install_path, find_data.cFileName) + 1;
272         if(layer_size < 0) {
273             free(registry_key);
274             return 40;
275         }
276         char* layer = malloc(layer_size);
277         snprintf(layer, layer_size, layer_pattern, install_path, find_data.cFileName);
278 
279         fprintf(log, "Adding explicit layer: %s\n", layer);
280 
281         DWORD zero = 0;
282         LSTATUS err = RegSetValueEx(hKey, layer, zero, REG_DWORD, (BYTE*) &zero, sizeof(DWORD));
283         free(layer);
284         if(err != ERROR_SUCCESS) {
285             free(registry_key);
286             return 50;
287         }
288     }
289 
290     RegCloseKey(hKey);
291     free(registry_key);
292     return 0;
293 }
294 
compare_versions(const struct SDKVersion * a,const struct SDKVersion * b)295 int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b)
296 {
297     // Compare numerical versions
298     for(int i = 0; i < 4; ++i) {
299         long* a_current = ((long*) a) + i;
300         long* b_current = ((long*) b) + i;
301 
302         if(*a_current < *b_current) {
303             return -4 + i;
304         } else if(*b_current < *a_current) {
305             return 4 - i;
306         }
307     }
308 
309     // An empty string should be considered greater (and therefore more recent) than one with test
310     if(a->extended[0] == '\0' && b->extended[0] != '\0') {
311         return 1;
312     } else if(b->extended[0] == '\0' && a->extended[0] != '\0') {
313         return -1;
314     }
315 
316     // Otherwise, just do a strncmp
317     return strncmp(a->extended, b->extended, SDK_VERSION_BUFFER_SIZE);
318 }
319 
find_installations(const char * api_name,char *** install_paths,struct SDKVersion ** install_versions,size_t * count)320 int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions, size_t* count)
321 {
322     *install_paths = malloc(sizeof(char*) * 64);
323     *install_versions = malloc(sizeof(struct SDKVersion) * 64);
324     *count = 0;
325 
326     // We want the 64-bit registries on 64-bit windows, and the 32-bit registries on 32-bit Windows.
327     // KEY_WOW64_64KEY accomplishes this because it gets ignored on 32-bit Windows.
328     HKEY hKey;
329     if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
330         0, KEY_READ | KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS) {
331         return 90;
332     }
333 
334     size_t api_len = strlen(api_name);
335     char* sdk_id = malloc(api_len + 4);
336     strcpy(sdk_id, api_name);
337     strcpy(sdk_id + api_len, "SDK");
338 
339     DWORD keyCount, keyLen;
340     RegQueryInfoKey(hKey, NULL, NULL, NULL, &keyCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
341     for(int i = 0; i < keyCount; ++i) {
342         TCHAR name[COPY_BUFFER_SIZE];
343         DWORD nameSize = COPY_BUFFER_SIZE;
344         RegEnumKeyEx(hKey, i, name, &nameSize, NULL, NULL, NULL, NULL);
345 
346         if(strncmp(sdk_id, name, api_len + 3)) {
347             continue;
348         }
349 
350         HKEY subKey;
351         if(RegOpenKeyEx(hKey, name, 0, KEY_READ | KEY_WOW64_64KEY, &subKey) != ERROR_SUCCESS) {
352             continue;
353         }
354 
355         bool found_installation = false, found_version = false;
356         DWORD valueCount;
357         RegQueryInfoKey(subKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL);
358         for(int j = 0; j < valueCount; ++j) {
359 
360             TCHAR name[COPY_BUFFER_SIZE], value[COPY_BUFFER_SIZE];
361             DWORD type, nameSize = COPY_BUFFER_SIZE, valSize = COPY_BUFFER_SIZE;
362             LSTATUS ret = RegEnumValue(subKey, j, name, &nameSize, NULL, &type, value, &valSize);
363             if(type == REG_SZ && !strcmp("InstallDir", name)) {
364                 *install_paths = realloc(*install_paths, sizeof(char*) * ((*count) + 1));
365                 (*install_paths)[*count] = malloc(sizeof(char) * COPY_BUFFER_SIZE);
366                 strcpy((*install_paths)[*count], value);
367                 found_installation = true;
368             } else if(type == REG_SZ && !strcmp("DisplayVersion", name)) {
369                 *install_versions = realloc(*install_versions, sizeof(struct SDKVersion) * ((*count) + 1));
370                 CHECK_ERROR(read_version(value, (*install_versions) + *count));
371                 found_version = true;
372             }
373 
374             if(found_installation && found_version) {
375                 ++(*count);
376                 break;
377             }
378         }
379         RegCloseKey(subKey);
380 
381         if(!(found_installation && found_version)) {
382             RegCloseKey(hKey);
383             free(sdk_id);
384             return 100;
385         }
386     }
387     RegCloseKey(hKey);
388     free(sdk_id);
389 
390     return 0;
391 }
392 
free_installations(char ** install_paths,struct SDKVersion * install_versions,size_t count)393 void free_installations(char** install_paths, struct SDKVersion* install_versions, size_t count)
394 {
395     for(size_t i = 0; i < count; ++i) {
396         free(install_paths[i]);
397     }
398     free(install_paths);
399     free(install_versions);
400 }
401 
parse_arguments(FILE * log,int argc,char ** argv,long * abi_major,const char ** api_name)402 int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name)
403 {
404     *abi_major = 0;
405     *api_name = "Vulkan";
406 
407     // Parse arguments
408     for(int i = 0; i < argc; ++i) {
409         if(!strcmp(argv[i], FLAG_ABI_MAJOR)) {
410             if(i + 1 == argc) {
411                 fprintf(log, "ERROR: No value given for flag %s.\n", FLAG_ABI_MAJOR);
412                 return 110;
413             }
414             *abi_major = strtol(argv[++i], NULL, 10);
415             if(*abi_major == 0) {
416                 fprintf(log, "ERROR: Unable to parse ABI major version as integer.\n");
417                 return 120;
418             }
419         }
420         if(!strcmp(argv[i], FLAG_API_NAME)) {
421             if(i + 1 == argc) {
422                 fprintf(log, "ERROR: No value given for flag %s.\n", FLAG_API_NAME);
423                 return 124;
424             }
425             *api_name = argv[++i];
426         }
427     }
428 
429     // Check that we have everything we need
430     if(*abi_major == 0 ) {
431         fprintf(log, "ERROR: Flag %s must be provided.\n", FLAG_ABI_MAJOR);
432         return 130;
433     }
434 
435     // It all worked fine
436     fprintf(log, "Found ABI: %ld\n\n", *abi_major);
437     return 0;
438 }
439 
read_version(const char * version_string,struct SDKVersion * version)440 int read_version(const char* version_string, struct SDKVersion* version)
441 {
442     size_t borders[4], dot_count = 0, i;
443     for(i = 0; dot_count < 3 && version_string[i] != '\0'; ++i) {
444         if(version_string[i] == '.') {
445             borders[dot_count++] = i + 1;
446         }
447     }
448     borders[3] = i + 1;
449 
450     if(dot_count < 3) {
451         return 140;
452     }
453 
454     // Read the version number
455     version->major = strtol(version_string,              NULL, 10);
456     version->minor = strtol(version_string + borders[0], NULL, 10);
457     version->patch = strtol(version_string + borders[1], NULL, 10);
458     version->build = strtol(version_string + borders[2], NULL, 10);
459 
460     strncpy(version->extended, version_string + borders[3] + 1,
461         min_s(SDK_VERSION_BUFFER_SIZE - 1, strlen(version_string + borders[3] + 1)));
462 
463     return 0;
464 }
465 
read_version_from_filename(const char * filename,struct SDKVersion * version)466 int read_version_from_filename(const char* filename, struct SDKVersion* version)
467 {
468     size_t borders[5], dash_count = 0;
469 
470     // Locate all of the dashes that divides different version numbers
471     size_t i;
472     for(i = 0; dash_count < 5; ++i) {
473         if(filename[i] == '-' && dash_count == 0) {
474             ++dash_count;
475         } else if(filename[i] == '-') {
476             borders[dash_count++ - 1] = i + 1;
477         } else if(filename[i] == '\0') {
478             return 150;
479         }
480     }
481     borders[4] = i + 1;
482 
483     // Read the version number
484     version->major = strtol(filename + borders[0], NULL, 10);
485     version->minor = strtol(filename + borders[1], NULL, 10);
486     version->patch = strtol(filename + borders[2], NULL, 10);
487     version->build = strtol(filename + borders[3], NULL, 10);
488 
489     if(strcmp(filename + borders[4] + 1, "dll") && strcmp(filename + borders[4] + 1, "exe")) {
490         strncpy(version->extended, filename + borders[4] + 1, SDK_VERSION_BUFFER_SIZE - 1);
491         size_t file_len = strlen(filename + borders[4] + 1);
492         if(file_len - 4 < SDK_VERSION_BUFFER_SIZE) {
493             version->extended[file_len - 4] = '\0';
494         }
495     } else {
496         version->extended[0] = '\0';
497     }
498 
499     for(size_t i = 0; version->extended[i] != '\0' && i < SDK_VERSION_BUFFER_SIZE; ++i) {
500         if(version->extended[i] == '-') {
501             version->extended[i] = '.';
502         }
503     }
504 
505     return 0;
506 }
507 
remove_explicit_layers(FILE * log,const char ** install_paths,size_t count,enum Platform platform,const char * api_name)508 int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform,
509     const char* api_name)
510 {
511     switch(platform)
512     {
513     case PLATFORM_X64:
514         fprintf(log, "Removing x64 explicit layers from registry\n");
515         break;
516     case PLATFORM_X86:
517         fprintf(log, "Removing x86 explicit layers from registry\n");
518         break;
519     }
520 
521     const char* pattern = "SOFTWARE\\Khronos\\%s\\ExplicitLayers";
522     int registry_size = snprintf(NULL, 0, pattern, api_name) + 1;
523     char* registry_key = malloc(registry_size);
524     snprintf(registry_key, registry_size, pattern, api_name);
525 
526     bool removed_one;
527     do {
528         // If this is a 32 bit system, we allow redirection to point this at the 32-bit registries.
529         // If not, we add the flag KEY_WOW64_64KEY, to disable redirection for this node.
530         HKEY hKey;
531         REGSAM flags = KEY_ALL_ACCESS;
532         if(platform == PLATFORM_X64) {
533             flags |= KEY_WOW64_64KEY;
534         }
535 
536         // Create (if needed) and open the explicit layer key
537         if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags,
538             NULL, &hKey, NULL) != ERROR_SUCCESS) {
539             free(registry_key);
540             return 160;
541         }
542 
543         removed_one = false;
544         DWORD valueCount;
545         RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, NULL, NULL, NULL, NULL);
546         for(DWORD i = 0; i < valueCount; ++i) {
547             TCHAR name[COPY_BUFFER_SIZE];
548             DWORD type, buffSize = COPY_BUFFER_SIZE;
549             RegEnumValue(hKey, i, name, &buffSize, NULL, &type, NULL, NULL);
550 
551             for(size_t j = 0; j < count; ++j) {
552                 if(strncmp(install_paths[j], name, strlen(install_paths[j])) == 0) {
553                     fprintf(log, "Removing explicit layer entry: %s\n", name);
554                     LSTATUS err = RegDeleteValue(hKey, name);
555                     if(err != ERROR_SUCCESS) {
556                         free(registry_key);
557                         return 170;
558                     }
559                     removed_one = true;
560                     break;
561                 }
562             }
563             if(removed_one) {
564                 break;
565             }
566         }
567 
568         RegCloseKey(hKey);
569     } while(removed_one);
570 
571     free(registry_key);
572     return 0;
573 }
574 
update_registry_layers(FILE * log,enum Platform platform,const struct SDKVersion * version,const char * api_name)575 int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version,
576     const char* api_name)
577 {
578     char** install_paths;
579     struct SDKVersion* install_versions;
580     size_t count;
581     CHECK_ERROR_HANDLED(find_installations(api_name, &install_paths, &install_versions, &count),
582         { free_installations(install_paths, install_versions, count); });
583     for(size_t i = 0; i < count; ++i) {
584         fprintf(log, "Found installation of %ld.%ld.%ld.%ld in: %s\n", install_versions[i].major,
585             install_versions[i].minor, install_versions[i].patch, install_versions[i].build, install_paths[i]);
586     }
587     fprintf(log, "\n");
588     if(platform == PLATFORM_X64) {
589         CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X64, api_name),
590             { free_installations(install_paths, install_versions, count); });
591         fprintf(log, "\n");
592     }
593     CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X86, api_name),
594         { free_installations(install_paths, install_versions, count); });
595     fprintf(log, "\n");
596 
597     if(version->major == 0 && version->minor == 0 && version->patch == 0 && version->build == 0) {
598         free_installations(install_paths, install_versions, count);
599         return 0;
600     }
601 
602     size_t preferred_sdk = count;
603     for(size_t i = 0; i < count; ++i) {
604         int cmp = compare_versions(install_versions + i, version);
605         if(cmp <= 0 && cmp >= -2) {
606             if(preferred_sdk == count ||
607                 (compare_versions(install_versions + i, install_versions + preferred_sdk) > 0)) {
608                 preferred_sdk = i;
609             }
610         }
611     }
612 
613     if(preferred_sdk < count) {
614         if(platform == PLATFORM_X64) {
615             CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[preferred_sdk], PLATFORM_X64, api_name),
616                 { free_installations(install_paths, install_versions, count); });
617             fprintf(log, "\n");
618         }
619         CHECK_ERROR_HANDLED(add_explicit_layers(log, install_paths[preferred_sdk], PLATFORM_X86, api_name),
620             { free_installations(install_paths, install_versions, count); });
621     }
622 
623     free_installations(install_paths, install_versions, count);
624     return 0;
625 }
626 
627 //int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
628 //    long abi_major, bool append_abi_major, struct SDKVersion* latest_version)
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)629 int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
630     long abi_major, bool leave_abi_major, struct SDKVersion* latest_version)
631 {
632     // Generate the filter string
633     const char* pattern = "%s%s-%ld-*-*-*-*%s";
634     int filter_size = snprintf(NULL, 0, pattern, path, name, abi_major, extension) + 1;
635     if(filter_size < 0) {
636         return 180;
637     }
638     char* filter = malloc(filter_size);
639     snprintf(filter, filter_size, pattern, path, name, abi_major, extension);
640 
641     // Find all of the files that match the pattern
642     char* latest_filename = malloc(64);
643     memset(latest_version, 0, sizeof(struct SDKVersion));
644     WIN32_FIND_DATA find_data;
645     HANDLE find = FindFirstFile(filter, &find_data);
646     free(filter);
647     for(bool at_end = (find != INVALID_HANDLE_VALUE); at_end;
648         at_end = FindNextFile(find, &find_data)) {
649 
650         struct SDKVersion version;
651         CHECK_ERROR_HANDLED(read_version_from_filename(find_data.cFileName, &version), { free(latest_filename); });
652 
653         // Decide if this is the latest file
654         if(compare_versions(latest_version, &version) < 0) {
655             *latest_version = version;
656             const char* latestPattern = "%s%s";
657             int size = snprintf(NULL, 0, latestPattern, path, find_data.cFileName) + 1;
658             if(size < 0) {
659                 free(latest_filename);
660                 return 200;
661             }
662             latest_filename = realloc(latest_filename, size);
663             snprintf(latest_filename, size, latestPattern, path, find_data.cFileName);
664         }
665     }
666     FindClose(find);
667 
668     // Make sure something was found
669     if(latest_version->major == 0 && latest_version->minor == 0 && latest_version->patch == 0 &&
670         latest_version->build == 0) {
671         fprintf(log, "Didn't find any version of %s%s\n", name, extension);
672         return 0;
673     }
674 
675     fprintf(log, "Found latest version of %s%s: %ld.%ld.%ld.%ld\n", name, extension, latest_version->major,
676         latest_version->minor, latest_version->patch, latest_version->build);
677 
678     // Generate output filename
679     char* output_filename;
680     if(leave_abi_major) {
681         const char* outPattern = "%s%s-%ld%s";
682         int out_size = snprintf(NULL, 0, outPattern, path, name, abi_major, extension) + 1;
683         if(out_size < 0) {
684             free(latest_filename);
685             return 205;
686         }
687         output_filename = malloc(out_size);
688         snprintf(output_filename, out_size, outPattern, path, name, abi_major, extension);
689     } else {
690         const char* outPattern = "%s%s%s";
691         int out_size = snprintf(NULL, 0, outPattern, path, name, extension) + 1;
692         if(out_size < 0) {
693             free(latest_filename);
694             return 210;
695         }
696         output_filename = malloc(out_size);
697         snprintf(output_filename, out_size, outPattern, path, name, extension);
698     }
699 
700     fprintf(stdout, "%s>%s;", latest_filename, output_filename);
701     free(latest_filename);
702     free(output_filename);
703     return 0;
704 }
705 
update_windows_directories(FILE * log,long abi_major,enum Platform platform,const char * api_name,struct SDKVersion * latest_runtime_version)706 int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name,
707     struct SDKVersion* latest_runtime_version)
708 {
709     size_t api_len = strlen(api_name);
710     char* vulkan_name = malloc(api_len + 1);
711     char* vulkan_info_name = malloc(api_len + 5);
712     for(size_t i = 0; i < api_len; ++i) {
713         vulkan_name[i] = tolower(api_name[i]);
714     }
715     vulkan_name[api_len] = '\0';
716     strcpy(vulkan_info_name, vulkan_name);
717     strcpy(vulkan_info_name + api_len, "info");
718 
719     struct SDKVersion version;
720     unsigned windows_path_size = GetWindowsDirectory(NULL, 0); // Size includes null terminator
721     char* system_path = malloc(windows_path_size +
722         max_s(strlen(PATH_SYSTEM32), strlen(PATH_SYSWOW64)));
723     GetWindowsDirectory(system_path, windows_path_size);
724 
725     strcpy(system_path + windows_path_size - 1, PATH_SYSTEM32);
726     fprintf(log, "Updating system directory: %s\n", system_path);
727     CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major, true,
728         latest_runtime_version), { free(system_path); });
729     CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major, false,
730         &version), { free(system_path); free(vulkan_info_name); free(vulkan_name);});
731     if(compare_versions(latest_runtime_version, &version) != 0) {
732         free(system_path);
733         free(vulkan_info_name);
734         free(vulkan_name);
735         return 220;
736     }
737 
738     if(platform == PLATFORM_X64) {
739         strcpy(system_path + windows_path_size - 1, PATH_SYSWOW64);
740         fprintf(log, "\nUpdating system directory: %s\n", system_path);
741         CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major,
742             true, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); });
743         if(compare_versions(latest_runtime_version, &version) != 0) {
744             free(system_path);
745             free(vulkan_info_name);
746             free(vulkan_name);
747             return 230;
748         }
749         CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major,
750             false, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); });
751         if(compare_versions(latest_runtime_version, &version) != 0) {
752             free(system_path);
753             free(vulkan_info_name);
754             free(vulkan_name);
755             return 240;
756         }
757     }
758 
759     free(system_path);
760     free(vulkan_info_name);
761     free(vulkan_name);
762     fprintf(log, "\nUpdate of system directories succeeded.\n\n");
763     return 0;
764 }
765