blob: d594bd3a836578141531abb2e58e4746b33e19fe [file] [log] [blame]
Lenny Komow10ec1212016-08-09 15:56:15 -06001/******************************************************************************
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 *
Lenny Komow769b9df2016-08-12 13:26:20 -060033 * The program must be called with the following parameters:
Lenny Komow10ec1212016-08-09 15:56:15 -060034 * --major-abi: A single number specifying the major abi version
Lenny Komow10ec1212016-08-09 15:56:15 -060035 */
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
Lenny Komow769b9df2016-08-12 13:26:20 -060054#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
Lenny Komow10ec1212016-08-09 15:56:15 -060058#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
63enum Platform
64{
65 PLATFORM_X64,
66 PLATFORM_X86,
67};
68
69#pragma pack(1)
70struct SDKVersion
71{
72 long major;
73 long minor;
74 long patch;
75 long build;
76 char extended[SDK_VERSION_BUFFER_SIZE];
77};
78
79const char* FLAG_ABI_MAJOR = "--abi-major";
Lenny Komowcaa3ab82016-11-04 10:38:39 -060080const char* FLAG_API_NAME = "--api-name";
Lenny Komow10ec1212016-08-09 15:56:15 -060081const char* PATH_SYSTEM32 = "\\SYSTEM32\\";
82const char* PATH_SYSWOW64 = "\\SysWOW64\\";
83
84inline size_t max_s(size_t a, size_t b) { return a > b ? a : b; }
85inline 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)
Lenny Komowcaa3ab82016-11-04 10:38:39 -060092// api_name (input) - The api name to use when working with registries
Lenny Komow10ec1212016-08-09 15:56:15 -060093// Returns: Zero on success, an error code on failure
Lenny Komowcaa3ab82016-11-04 10:38:39 -060094int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name);
Lenny Komow10ec1212016-08-09 15:56:15 -060095
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
99int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b);
100
Lenny Komow10ec1212016-08-09 15:56:15 -0600101// Locate all of the SDK installations
102//
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600103// api_name (input) - The api name to use when working with registries
Lenny Komow10ec1212016-08-09 15:56:15 -0600104// 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]
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600113int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions,
114 size_t* count);
Lenny Komow10ec1212016-08-09 15:56:15 -0600115
116// Free the memory allocated by find_installations()
117void 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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600125// api_name (output) - The api name to use when working with registries and system files
Lenny Komow10ec1212016-08-09 15:56:15 -0600126// Returns: Zero on success, an error code on failure
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600127int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600128
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
134int 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
142int 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)
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600150// api_name (input) - The api name to use when working with registries
Lenny Komow10ec1212016-08-09 15:56:15 -0600151// Returns: Zero on success, an error code on failure
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600152int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform,
153 const char* api_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600154
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)
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600160// api_name (input) - The api name to use when working with registries
Lenny Komow10ec1212016-08-09 15:56:15 -0600161// Returns: Zero on success, an error code on failure
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600162int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version,
163 const char* api_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600164
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
Lenny Komow28e06f72016-08-17 14:50:13 -0600172// append_abi_major (input) - Whether or not the ABI number should be appended to the filename
Lenny Komow10ec1212016-08-09 15:56:15 -0600173// latest_version (output) - The version of the runtime which the file was updated to
174// Returns: Zero on success, an error code on failure
175int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
Lenny Komow28e06f72016-08-17 14:50:13 -0600176 long abi_major, bool append_abi_major, struct SDKVersion* latest_version);
Lenny Komow10ec1212016-08-09 15:56:15 -0600177
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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600183// api_name (input) - The api name to use when working with system files
Lenny Komow10ec1212016-08-09 15:56:15 -0600184// latest_runtime_version (output) - The version that the runtime files were updated to
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600185int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name,
Lenny Komow10ec1212016-08-09 15:56:15 -0600186 struct SDKVersion* latest_runtime_version);
187
188int 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;
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600202 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);
Lenny Komow10ec1212016-08-09 15:56:15 -0600206
207 // This makes System32 and SysWOW64 not do any redirection (well, until 128-bit is a thing)
Lenny Komowd8a62412017-01-16 10:10:47 -0700208 PVOID useless;
209 Wow64DisableWow64FsRedirection(&useless);
Lenny Komow10ec1212016-08-09 15:56:15 -0600210
211 // Update System32 (on all systems) and SysWOW64 on 64-bit system
212 struct SDKVersion latest_runtime_version;
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600213 CHECK_ERROR_HANDLED(update_windows_directories(log, abi_major, platform, api_name,
214 &latest_runtime_version), { fclose(log); });
Lenny Komow10ec1212016-08-09 15:56:15 -0600215
216 // Update the explicit layers that are set in the windows registry
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600217 CHECK_ERROR_HANDLED(update_registry_layers(log, platform, &latest_runtime_version, api_name),
218 { fclose(log); });
Lenny Komow10ec1212016-08-09 15:56:15 -0600219
220 fclose(log);
221 return 0;
222}
223
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600224int add_explicit_layers(FILE* log, const char* install_path, enum Platform platform, const char* api_name)
Lenny Komow10ec1212016-08-09 15:56:15 -0600225{
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 }
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600235
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);
Lenny Komow10ec1212016-08-09 15:56:15 -0600240
Lenny Komow769b9df2016-08-12 13:26:20 -0600241 // 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.
Lenny Komow10ec1212016-08-09 15:56:15 -0600243 HKEY hKey;
244 REGSAM flags = KEY_ALL_ACCESS;
245 if(platform == PLATFORM_X64) {
246 flags |= KEY_WOW64_64KEY;
247 }
Lenny Komow28e06f72016-08-17 14:50:13 -0600248
249 // Create (if needed) and open the explicit layer key
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600250 if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags,
251 NULL, &hKey, NULL) != ERROR_SUCCESS) {
252 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600253 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) {
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600273 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600274 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) {
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600285 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600286 return 50;
287 }
288 }
289
290 RegCloseKey(hKey);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600291 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600292 return 0;
293}
294
295int compare_versions(const struct SDKVersion* a, const struct SDKVersion* b)
296{
Lenny Komow21fe80e2016-09-02 14:05:45 -0600297 // Compare numerical versions
Lenny Komow10ec1212016-08-09 15:56:15 -0600298 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
Lenny Komow21fe80e2016-09-02 14:05:45 -0600309 // 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
Lenny Komow10ec1212016-08-09 15:56:15 -0600317 return strncmp(a->extended, b->extended, SDK_VERSION_BUFFER_SIZE);
318}
319
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600320int find_installations(const char* api_name, char*** install_paths, struct SDKVersion** install_versions, size_t* count)
Lenny Komow10ec1212016-08-09 15:56:15 -0600321{
322 *install_paths = malloc(sizeof(char*) * 64);
323 *install_versions = malloc(sizeof(struct SDKVersion) * 64);
324 *count = 0;
325
Lenny Komow769b9df2016-08-12 13:26:20 -0600326 // 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.
Lenny Komow10ec1212016-08-09 15:56:15 -0600328 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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600334 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
Lenny Komow10ec1212016-08-09 15:56:15 -0600339 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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600346 if(strncmp(sdk_id, name, api_len + 3)) {
Lenny Komow10ec1212016-08-09 15:56:15 -0600347 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];
Lenny Komowd8a62412017-01-16 10:10:47 -0700361 DWORD type, nameSize = COPY_BUFFER_SIZE, valSize = COPY_BUFFER_SIZE;
362 LSTATUS ret = RegEnumValue(subKey, j, name, &nameSize, NULL, &type, value, &valSize);
Lenny Komow10ec1212016-08-09 15:56:15 -0600363 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;
Lenny Komowd8a62412017-01-16 10:10:47 -0700368 } else if(type == REG_SZ && !strcmp("DisplayVersion", name)) {
Lenny Komow10ec1212016-08-09 15:56:15 -0600369 *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);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600383 free(sdk_id);
Lenny Komow10ec1212016-08-09 15:56:15 -0600384 return 100;
385 }
386 }
387 RegCloseKey(hKey);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600388 free(sdk_id);
Lenny Komow10ec1212016-08-09 15:56:15 -0600389
390 return 0;
391}
392
393void 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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600402int parse_arguments(FILE* log, int argc, char** argv, long* abi_major, const char** api_name)
Lenny Komow10ec1212016-08-09 15:56:15 -0600403{
404 *abi_major = 0;
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600405 *api_name = "Vulkan";
Lenny Komow10ec1212016-08-09 15:56:15 -0600406
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 }
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600420 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 }
Lenny Komow10ec1212016-08-09 15:56:15 -0600427 }
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
440int 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
466int 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
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600508int remove_explicit_layers(FILE* log, const char** install_paths, size_t count, enum Platform platform,
509 const char* api_name)
Lenny Komow10ec1212016-08-09 15:56:15 -0600510{
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 }
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600520
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);
Lenny Komow10ec1212016-08-09 15:56:15 -0600525
526 bool removed_one;
527 do {
Lenny Komow769b9df2016-08-12 13:26:20 -0600528 // 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.
Lenny Komow10ec1212016-08-09 15:56:15 -0600530 HKEY hKey;
531 REGSAM flags = KEY_ALL_ACCESS;
532 if(platform == PLATFORM_X64) {
533 flags |= KEY_WOW64_64KEY;
534 }
Lenny Komow28e06f72016-08-17 14:50:13 -0600535
536 // Create (if needed) and open the explicit layer key
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600537 if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry_key, 0, NULL, REG_OPTION_NON_VOLATILE, flags,
538 NULL, &hKey, NULL) != ERROR_SUCCESS) {
539 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600540 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) {
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600556 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600557 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);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600570
571 free(registry_key);
Lenny Komow10ec1212016-08-09 15:56:15 -0600572 return 0;
573}
574
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600575int update_registry_layers(FILE* log, enum Platform platform, const struct SDKVersion* version,
576 const char* api_name)
Lenny Komow10ec1212016-08-09 15:56:15 -0600577{
578 char** install_paths;
579 struct SDKVersion* install_versions;
580 size_t count;
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600581 CHECK_ERROR_HANDLED(find_installations(api_name, &install_paths, &install_versions, &count),
Lenny Komow10ec1212016-08-09 15:56:15 -0600582 { 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) {
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600589 CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X64, api_name),
Lenny Komow10ec1212016-08-09 15:56:15 -0600590 { free_installations(install_paths, install_versions, count); });
591 fprintf(log, "\n");
592 }
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600593 CHECK_ERROR_HANDLED(remove_explicit_layers(log, install_paths, count, PLATFORM_X86, api_name),
Lenny Komow10ec1212016-08-09 15:56:15 -0600594 { 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
Lenny Komowff57fc12017-01-13 14:20:57 -0700602 size_t preferred_sdk = count;
Lenny Komow10ec1212016-08-09 15:56:15 -0600603 for(size_t i = 0; i < count; ++i) {
Lenny Komowff57fc12017-01-13 14:20:57 -0700604 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;
Lenny Komow10ec1212016-08-09 15:56:15 -0600609 }
Lenny Komow10ec1212016-08-09 15:56:15 -0600610 }
611 }
Lenny Komowff57fc12017-01-13 14:20:57 -0700612
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
Lenny Komow10ec1212016-08-09 15:56:15 -0600623 free_installations(install_paths, install_versions, count);
624 return 0;
625}
626
Lenny Komow28e06f72016-08-17 14:50:13 -0600627//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)
Lenny Komow10ec1212016-08-09 15:56:15 -0600629int update_system_file(FILE* log, const char* name, const char* extension, const char* path,
Lenny Komow769b9df2016-08-12 13:26:20 -0600630 long abi_major, bool leave_abi_major, struct SDKVersion* latest_version)
Lenny Komow10ec1212016-08-09 15:56:15 -0600631{
632 // Generate the filter string
Lenny Komow769b9df2016-08-12 13:26:20 -0600633 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;
Lenny Komow10ec1212016-08-09 15:56:15 -0600637 }
Lenny Komow769b9df2016-08-12 13:26:20 -0600638 char* filter = malloc(filter_size);
639 snprintf(filter, filter_size, pattern, path, name, abi_major, extension);
Lenny Komow10ec1212016-08-09 15:56:15 -0600640
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;
Lenny Komow769b9df2016-08-12 13:26:20 -0600656 const char* latestPattern = "%s%s";
657 int size = snprintf(NULL, 0, latestPattern, path, find_data.cFileName) + 1;
Lenny Komow10ec1212016-08-09 15:56:15 -0600658 if(size < 0) {
659 free(latest_filename);
660 return 200;
661 }
662 latest_filename = realloc(latest_filename, size);
Lenny Komow769b9df2016-08-12 13:26:20 -0600663 snprintf(latest_filename, size, latestPattern, path, find_data.cFileName);
Lenny Komow10ec1212016-08-09 15:56:15 -0600664 }
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
Lenny Komow769b9df2016-08-12 13:26:20 -0600679 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);
Lenny Komow10ec1212016-08-09 15:56:15 -0600698 }
Lenny Komow10ec1212016-08-09 15:56:15 -0600699
700 // Remove any older version of the output file
701 if(remove(output_filename) == 0) {
702 fprintf(log, "Removed file %s\n", output_filename);
703 } else {
704 fprintf(log, "Did not remove file %s\n", output_filename);
705 }
706
707 fprintf(log, "Attempting to copy file %s to %s\n", latest_filename, output_filename);
Lenny Komow769b9df2016-08-12 13:26:20 -0600708 if(CopyFile(latest_filename, output_filename, false) == 0) {
709 free(latest_filename);
710 free(output_filename);
711 return 215;
712 }
Lenny Komow10ec1212016-08-09 15:56:15 -0600713
714 free(latest_filename);
Lenny Komow769b9df2016-08-12 13:26:20 -0600715 free(output_filename);
Lenny Komow10ec1212016-08-09 15:56:15 -0600716 return 0;
717}
718
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600719int update_windows_directories(FILE* log, long abi_major, enum Platform platform, const char* api_name,
720 struct SDKVersion* latest_runtime_version)
Lenny Komow10ec1212016-08-09 15:56:15 -0600721{
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600722 size_t api_len = strlen(api_name);
723 char* vulkan_name = malloc(api_len + 1);
724 char* vulkan_info_name = malloc(api_len + 5);
725 for(size_t i = 0; i < api_len; ++i) {
726 vulkan_name[i] = tolower(api_name[i]);
727 }
728 vulkan_name[api_len] = '\0';
729 strcpy(vulkan_info_name, vulkan_name);
730 strcpy(vulkan_info_name + api_len, "info");
731
Lenny Komow10ec1212016-08-09 15:56:15 -0600732 struct SDKVersion version;
733 unsigned windows_path_size = GetWindowsDirectory(NULL, 0); // Size includes null terminator
734 char* system_path = malloc(windows_path_size +
735 max_s(strlen(PATH_SYSTEM32), strlen(PATH_SYSWOW64)));
736 GetWindowsDirectory(system_path, windows_path_size);
737
738 strcpy(system_path + windows_path_size - 1, PATH_SYSTEM32);
739 fprintf(log, "Updating system directory: %s\n", system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600740 CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major, true,
Lenny Komow10ec1212016-08-09 15:56:15 -0600741 latest_runtime_version), { free(system_path); });
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600742 CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major, false,
743 &version), { free(system_path); free(vulkan_info_name); free(vulkan_name);});
Lenny Komow10ec1212016-08-09 15:56:15 -0600744 if(compare_versions(latest_runtime_version, &version) != 0) {
Lenny Komow769b9df2016-08-12 13:26:20 -0600745 free(system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600746 free(vulkan_info_name);
747 free(vulkan_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600748 return 220;
749 }
750
751 if(platform == PLATFORM_X64) {
752 strcpy(system_path + windows_path_size - 1, PATH_SYSWOW64);
753 fprintf(log, "\nUpdating system directory: %s\n", system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600754 CHECK_ERROR_HANDLED(update_system_file(log, vulkan_name, ".dll", system_path, abi_major,
755 true, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); });
Lenny Komow10ec1212016-08-09 15:56:15 -0600756 if(compare_versions(latest_runtime_version, &version) != 0) {
Lenny Komow769b9df2016-08-12 13:26:20 -0600757 free(system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600758 free(vulkan_info_name);
759 free(vulkan_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600760 return 230;
761 }
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600762 CHECK_ERROR_HANDLED(update_system_file(log, vulkan_info_name, ".exe", system_path, abi_major,
763 false, &version), { free(system_path); free(vulkan_info_name); free(vulkan_name); });
Lenny Komow10ec1212016-08-09 15:56:15 -0600764 if(compare_versions(latest_runtime_version, &version) != 0) {
Lenny Komow769b9df2016-08-12 13:26:20 -0600765 free(system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600766 free(vulkan_info_name);
767 free(vulkan_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600768 return 240;
769 }
770 }
771
772 free(system_path);
Lenny Komowcaa3ab82016-11-04 10:38:39 -0600773 free(vulkan_info_name);
774 free(vulkan_name);
Lenny Komow10ec1212016-08-09 15:56:15 -0600775 fprintf(log, "\nUpdate of system directories succeeded.\n\n");
776 return 0;
777}