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