• 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* PATH_SYSTEM32 = "\\SYSTEM32\\";
81 const char* PATH_SYSWOW64 = "\\SysWOW64\\";
82 
max_s(size_t a,size_t b)83 inline size_t max_s(size_t a, size_t b) { return a > b ? a : b; }
min_s(size_t a,size_t 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 
main(int argc,char ** argv)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 
add_explicit_layers(FILE * log,const char * install_path,enum Platform platform)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 
compare_versions(const struct SDKVersion * a,const struct SDKVersion * b)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 
find_installations(char *** install_paths,struct SDKVersion ** install_versions,size_t * count)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 
free_installations(char ** install_paths,struct SDKVersion * install_versions,size_t count)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 
parse_arguments(FILE * log,int argc,char ** argv,long * abi_major)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 
read_version(const char * version_string,struct SDKVersion * version)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 
read_version_from_filename(const char * filename,struct SDKVersion * version)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 
remove_explicit_layers(FILE * log,const char ** install_paths,size_t count,enum Platform platform)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 
update_registry_layers(FILE * log,enum Platform platform,const struct SDKVersion * version)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)
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)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 
update_windows_directories(FILE * log,long abi_major,enum Platform platform,struct SDKVersion * latest_runtime_version)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