diff --git a/res/loc/rufus.loc b/res/loc/rufus.loc index 0eb5f04d..7d948de0 100644 --- a/res/loc/rufus.loc +++ b/res/loc/rufus.loc @@ -590,6 +590,8 @@ t MSG_329 "Remove requirement for 4GB+ RAM and 64GB+ disk" t MSG_330 "Remove requirement for an online Microsoft account" t MSG_331 "Disable data collection (Skip privacy questions)" t MSG_332 "Prevent Windows To Go from accessing internal disks" +t MSG_333 "Set a local account using the same name as this user's" +t MSG_334 "Set regional options to the same values as this user's" ######################################################################### l "ar-SA" "Arabic (العربية)" 0x0401, 0x0801, 0x0c01, 0x1001, 0x1401, 0x1801, 0x1c01, 0x2001, 0x2401, 0x2801, 0x2c01, 0x3001, 0x3401, 0x3801, 0x3c01, 0x4001 diff --git a/src/format.c b/src/format.c index 3268c49f..3c7a7529 100644 --- a/src/format.c +++ b/src/format.c @@ -71,7 +71,7 @@ extern const int nb_steps[FS_MAX]; extern uint32_t dur_mins, dur_secs; extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files; static int actual_fs_type, wintogo_index = -1, wininst_index = 0; -extern int unattend_xml_selection; +extern int unattend_xml_flags; extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing; extern BOOL write_as_image, use_vds, write_as_esp, is_vds_available; extern const grub_patch_t grub_patch[2]; @@ -1472,7 +1472,7 @@ static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp) // "upgrade" the ReFS version on all drives to v3.7, thereby preventing you from being able to mount // those volumes back on Windows 10 ever again. Yes, I have been stung by this Microsoft bullshit! // See: https://gist.github.com/0xbadfca11/da0598e47dd643d933dc#Mountability - if (unattend_xml_selection & UNATTEND_OFFLINE_INTERNAL_DRIVES) { + if (unattend_xml_flags & UNATTEND_OFFLINE_INTERNAL_DRIVES) { uprintf("Setting the target's internal drives offline using command:"); // This applies the "offlineServicing" section of the unattend.xml (while ignoring the other sections) static_sprintf(cmd, "dism /Image:%s\\ /Apply-Unattend:%s", drive_name, unattend_xml_path); @@ -1498,9 +1498,9 @@ static BOOL SetupWinToGo(DWORD DriveIndex, const char* drive_name, BOOL use_esp) /* * Add unattend.xml to 'sources\boot.wim' (install) or 'Windows\Panther\' (Windows To Go) - * NB: Work with a copy of unattend_xml_selection as a paremeter since we will modify it. + * NB: Work with a copy of unattend_xml_flags as a paremeter since we will modify it. */ -BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) +BOOL ApplyWindowsCustomization(char drive_letter, int flags) { BOOL r = FALSE, is_hive_mounted = FALSE; int i; @@ -1516,7 +1516,7 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) assert(unattend_xml_path != NULL); uprintf("Applying Windows customization:"); - if (unattend_selection & UNATTEND_WINDOWS_TO_GO) { + if (flags & UNATTEND_WINDOWS_TO_GO) { static_sprintf(path, "%c:\\Windows\\Panther", drive_letter); if (!CreateDirectoryA(path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { uprintf("Could not create '%s' : %s", path, WindowsErrorString()); @@ -1530,7 +1530,7 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) uprintf("Added '%s'", path); } else { boot_wim_path[0] = drive_letter; - if (unattend_selection & UNATTEND_WINPE_SETUP_MASK) { + if (flags & UNATTEND_WINPE_SETUP_MASK) { // Create a backup of sources\appraiserres.dll and then create an empty file to // allow in-place upgrades without TPM/SB. Note that we need to create an empty, // appraiserres.dll otherwise setup.exe extracts its own. @@ -1547,14 +1547,14 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 0, PATCH_PROGRESS_TOTAL); // We only need to mount boot.wim if we have windowsPE data to deal with. If // not, we can just copy our unattend.xml in \sources\$OEM$\$$\Panther\. - if (unattend_selection & UNATTEND_WINPE_SETUP_MASK) { + if (flags & UNATTEND_WINPE_SETUP_MASK) { uprintf("Mounting '%s'...", boot_wim_path); mount_path = WimMountImage(boot_wim_path, wim_index); if (mount_path == NULL) goto out; } - if (unattend_selection & (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_MINRAM_MINDISK_MASK)) { + if (flags & (UNATTEND_SECUREBOOT_TPM | UNATTEND_MINRAM_MINDISK)) { // Try to create the registry keys directly, and fallback to using unattend // if that fails (which the Windows Store version is expected to do). static_sprintf(path, "%s\\Windows\\System32\\config\\SYSTEM", mount_path); @@ -1582,7 +1582,7 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) } for (i = 0; i < ARRAYSIZE(bypass_name); i++) { - if (!(unattend_selection & (1 << (i / 2)))) + if (!(flags & (1 << (i / 2)))) continue; status = RegSetValueExA(hSubKey, bypass_name[i], 0, REG_DWORD, (LPBYTE)&dwVal, sizeof(DWORD)); if (status != ERROR_SUCCESS) { @@ -1596,11 +1596,11 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) // We were successfull in creating the keys so disable the windowsPE section from unattend.xml // We do this by replacing '' with '' // (provided that the registry key creation was the only item for this pass) - if ((unattend_selection & UNATTEND_WINPE_SETUP_MASK) == (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_MINRAM_MINDISK_MASK)) { + if ((flags & UNATTEND_WINPE_SETUP_MASK) == (UNATTEND_SECUREBOOT_TPM | UNATTEND_MINRAM_MINDISK)) { if (replace_in_token_data(unattend_xml_path, " entries from windowsPE (and only windowsPE). @@ -1610,7 +1610,7 @@ BOOL ApplyWindowsCustomization(char drive_letter, int unattend_selection) } copy_unattend: - if (unattend_selection & UNATTEND_WINPE_SETUP_MASK) { + if (flags & UNATTEND_WINPE_SETUP_MASK) { // If we have a windowsPE section, copy the answer files to the root of boot.wim as // Autounattend.xml. This also results in that file being automatically copied over // to %WINDIR%\Panther\unattend.xml for later passes processing. @@ -2452,7 +2452,7 @@ DWORD WINAPI FormatThread(void* param) goto out; } if (unattend_xml_path != NULL) { - if (!ApplyWindowsCustomization(drive_name[0], unattend_xml_selection | UNATTEND_WINDOWS_TO_GO)) + if (!ApplyWindowsCustomization(drive_name[0], unattend_xml_flags | UNATTEND_WINDOWS_TO_GO)) FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_PATCH); } } else { @@ -2494,7 +2494,7 @@ DWORD WINAPI FormatThread(void* param) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_PATCH); } if (unattend_xml_path != NULL) { - if (!ApplyWindowsCustomization(drive_name[0], unattend_xml_selection)) + if (!ApplyWindowsCustomization(drive_name[0], unattend_xml_flags)) FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_PATCH); } } diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 779a7d49..19a14429 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -6,7 +6,7 @@ * * See also: https://utf8everywhere.org * - * Copyright © 2010-2021 Pete Batard + * Copyright © 2010-2022 Pete Batard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1240,6 +1240,33 @@ static __inline BOOL SetupDiGetDeviceRegistryPropertyU(HDEVINFO DeviceInfoSet, P return ret; } +// NB: This does not support the ERROR_INSUFFICIENT_BUFFER dance to retrieve the required buffer size +static __inline BOOL GetUserNameU(LPSTR lpBuffer, LPDWORD pcbBuffer) +{ + BOOL ret; + DWORD err, size; + if (lpBuffer == NULL || pcbBuffer == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + size = *pcbBuffer; + // coverity[returned_null] + walloc(lpBuffer, size); + ret = GetUserNameW(wlpBuffer, &size); + err = GetLastError(); + if (ret) { + *pcbBuffer = (DWORD)wchar_to_utf8_no_alloc(wlpBuffer, lpBuffer, size); + if (*pcbBuffer == 0) + err = GetLastError(); + else + // Reported size includes the NUL terminator + (*pcbBuffer)++; + } + wfree(lpBuffer); + SetLastError(err); + return ret; +} + static __inline BOOL GetVolumeInformationU(LPCSTR lpRootPathName, LPSTR lpVolumeNameBuffer, DWORD nVolumeNameSize, LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags, LPSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize) diff --git a/src/registry.h b/src/registry.h index aad6d950..db85ee3e 100644 --- a/src/registry.h +++ b/src/registry.h @@ -1,7 +1,7 @@ /* * Rufus: The Reliable USB Formatting Utility * Registry access - * Copyright © 2012-2015 Pete Batard + * Copyright © 2012-2022 Pete Batard * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ */ #include #include +#include #include "rufus.h" #pragma once @@ -36,9 +37,15 @@ static __inline BOOL DeleteRegistryKey(HKEY key_root, const char* key_name) HKEY hSoftware = NULL; LONG s; - if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ|KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) { + assert(key_root == REGKEY_HKCU); + if (key_root != REGKEY_HKCU) + return FALSE; + assert(key_name != NULL); + if (key_name == NULL) + return FALSE; + + if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ|KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) return FALSE; - } s = RegDeleteKeyA(hSoftware, key_name); if ((s != ERROR_SUCCESS) && (s != ERROR_FILE_NOT_FOUND)) { @@ -68,7 +75,6 @@ static __inline BOOL IsRegistryNode(HKEY key_root, const char* key_name) static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type, LPBYTE dest, DWORD dest_size) { - const char software_prefix[] = "SOFTWARE\\"; char long_key_name[MAX_PATH] = { 0 }; BOOL r = FALSE; size_t i; @@ -81,25 +87,17 @@ static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD if (key_name == NULL) return FALSE; - for (i=safe_strlen(key_name); i>0; i--) { + for (i = safe_strlen(key_name); i > 0; i--) { if (key_name[i] == '\\') break; } if (i > 0) { - // Prefix with "SOFTWARE" if needed - if (_strnicmp(key_name, software_prefix, sizeof(software_prefix) - 1) != 0) { - if (i + sizeof(software_prefix) >= sizeof(long_key_name)) - return FALSE; - strcpy(long_key_name, software_prefix); - static_strcat(long_key_name, key_name); - long_key_name[sizeof(software_prefix) + i - 1] = 0; - } else { - if (i >= sizeof(long_key_name)) - return FALSE; - static_strcpy(long_key_name, key_name); - long_key_name[i] = 0; - } + // For a read operation, allow access to any long key + if (i >= sizeof(long_key_name)) + return FALSE; + static_strcpy(long_key_name, key_name); + long_key_name[i] = 0; i++; if (RegOpenKeyExA(key_root, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) { hApp = NULL; @@ -133,57 +131,34 @@ out: /* Write a generic registry key value (create the key if it doesn't exist) */ static __inline BOOL _SetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type, LPBYTE src, DWORD src_size) { - const char software_prefix[] = "SOFTWARE\\"; - char long_key_name[MAX_PATH] = { 0 }; BOOL r = FALSE; - size_t i; HKEY hRoot = NULL, hApp = NULL; DWORD dwDisp, dwType = reg_type; + assert(key_name != NULL); if (key_name == NULL) return FALSE; + assert(key_root == REGKEY_HKCU); + if (key_root != REGKEY_HKCU) + return FALSE; + // Validate that we are always dealing with a short key + assert(strchr(key_name, '\\') == NULL); + if (strchr(key_name, '\\') != NULL) + return FALSE; if (RegOpenKeyExA(key_root, NULL, 0, KEY_READ|KEY_CREATE_SUB_KEY, &hRoot) != ERROR_SUCCESS) { hRoot = NULL; goto out; } - // Find if we're dealing with a short key - for (i = safe_strlen(key_name); i > 0; i--) { - if (key_name[i] == '\\') - break; + // This is a short key name, store the value under our app sub-hive + if (RegCreateKeyExA(hRoot, "SOFTWARE\\" COMPANY_NAME "\\" APPLICATION_NAME, 0, NULL, 0, + KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY, NULL, &hApp, &dwDisp) != ERROR_SUCCESS) { + hApp = NULL; + goto out; } - if (i > 0) { - // Prefix with "SOFTWARE" if needed - if (_strnicmp(key_name, software_prefix, sizeof(software_prefix) - 1) != 0) { - if (i + sizeof(software_prefix) >= sizeof(long_key_name)) - goto out; - strcpy(long_key_name, software_prefix); - static_strcat(long_key_name, key_name); - long_key_name[sizeof(software_prefix) + i - 1] = 0; - } else { - if (i >= sizeof(long_key_name)) - goto out; - static_strcpy(long_key_name, key_name); - long_key_name[i] = 0; - } - i++; - if (RegCreateKeyExA(hRoot, long_key_name, 0, NULL, 0, - KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY, NULL, &hApp, &dwDisp) != ERROR_SUCCESS) { - hApp = NULL; - goto out; - } - } else { - // This is a short key name, store the value under our app sub-hive - if (RegCreateKeyExA(hRoot, "SOFTWARE\\" COMPANY_NAME "\\" APPLICATION_NAME, 0, NULL, 0, - KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY, NULL, &hApp, &dwDisp) != ERROR_SUCCESS) { - hApp = NULL; - goto out; - } - } - - r = (RegSetValueExA(hApp, &key_name[i], 0, dwType, src, src_size) == ERROR_SUCCESS); + r = (RegSetValueExA(hApp, key_name, 0, dwType, src, src_size) == ERROR_SUCCESS); out: if (hRoot != NULL) diff --git a/src/rufus.c b/src/rufus.c index 83841694..6ae2fe64 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -78,7 +78,7 @@ static BOOL app_changed_label = FALSE; static BOOL allowed_filesystem[FS_MAX] = { 0 }; static int64_t last_iso_blocking_status; static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNOWN; -static int image_index = 0, select_index = 0, unattend_xml_mask = UNATTEND_DEFAULT_SELECTION; +static int image_index = 0, select_index = 0, unattend_xml_mask = UNATTEND_DEFAULT_SELECTION_MASK; static RECT relaunch_rc = { -65536, -65536, 0, 0}; static UINT uMBRChecked = BST_UNCHECKED; static HANDLE format_thread = NULL; @@ -125,7 +125,7 @@ BOOL write_as_image = FALSE, write_as_esp = FALSE, use_vds = FALSE, ignore_boot_ BOOL appstore_version = FALSE, is_vds_available = TRUE; float fScale = 1.0f; int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0; -int default_fs, fs_type, boot_type, partition_type, target_type, unattend_xml_selection = 0; +int default_fs, fs_type, boot_type, partition_type, target_type, unattend_xml_flags = 0; int force_update = 0, default_thread_priority = THREAD_PRIORITY_ABOVE_NORMAL; char szFolderPath[MAX_PATH], app_dir[MAX_PATH], system_dir[MAX_PATH], temp_dir[MAX_PATH], sysnative_dir[MAX_PATH]; char app_data_dir[MAX_PATH], user_dir[MAX_PATH]; @@ -1250,14 +1250,21 @@ out: return ret; } -static char* CreateUnattendXml(int arch, int mask) +/// +/// Create an installation answer file containing the sections specified by the flags. +/// +/// The processor architecture of the Windows image being used. +/// A bitmask representing the sections to enable. +/// See "Windows User Experience flags and masks" from in rufus.h +/// The path of a newly created answer file on success or NULL on error. +static char* CreateUnattendXml(int arch, int flags) { static char path[MAX_PATH]; FILE* fd; int i, order; const char* xml_arch_names[5] = { "x86", "amd64", "arm", "arm64" }; - unattend_xml_selection = mask; - if (arch < ARCH_X86_32 || arch >= ARCH_ARM_64 || mask == 0) + unattend_xml_flags = flags; + if (arch < ARCH_X86_32 || arch >= ARCH_ARM_64 || flags == 0) return NULL; arch--; // coverity[swapped_arguments] @@ -1274,7 +1281,7 @@ static char* CreateUnattendXml(int arch, int mask) // as alters the layout and options of the initial Windows installer screens, which may scare users. // So, in format.c, we'll try to insert the registry keys directly and drop this section. However, // because Microsoft prevents Store apps from editing an offline registry, we do need this fallback. - if (mask & UNATTEND_WINPE_SETUP_MASK) { + if (flags & UNATTEND_WINPE_SETUP_MASK) { order = 1; fprintf(fd, " \n"); fprintf(fd, " \n"); fprintf(fd, " \n"); for (i = 0; i < ARRAYSIZE(bypass_name); i++) { - if (!(mask & (1 << (i/2)))) + if (!(flags & (1 << (i/2)))) continue; fprintf(fd, " \n"); fprintf(fd, " %d\n", order++); @@ -1300,7 +1307,7 @@ static char* CreateUnattendXml(int arch, int mask) fprintf(fd, " \n"); } - if (mask & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) { + if (flags & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) { order = 1; fprintf(fd, " \n"); fprintf(fd, " \n", xml_arch_names[arch]); fprintf(fd, " \n"); // This part was picked from https://github.com/AveYo/MediaCreationTool.bat/blob/main/bypass11/AutoUnattend.xml - if (mask & UNATTEND_NO_ONLINE_ACCOUNT_MASK) { + if (flags & UNATTEND_NO_ONLINE_ACCOUNT) { fprintf(fd, " \n"); fprintf(fd, " %d\n", order++); fprintf(fd, " reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f\n"); @@ -1319,27 +1326,82 @@ static char* CreateUnattendXml(int arch, int mask) fprintf(fd, " \n"); } - if (mask & UNATTEND_OOBE_SHELL_SETUP) { + if (flags & UNATTEND_OOBE_MASK) { order = 1; fprintf(fd, " \n"); - fprintf(fd, " \n", xml_arch_names[arch]); - // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-oobe-protectyourpc - // It is really super insidous of Microsoft to call this option "ProtectYourPC", when it's really only about - // data collection. But of course, if it was called "AllowDataCollection", everyone would turn it off... - if (mask & UNATTEND_NO_DATA_COLLECTION_MASK) { - fprintf(fd, " \n"); - fprintf(fd, " 3\n"); - fprintf(fd, " \n"); + if (flags & UNATTEND_OOBE_SHELL_SETUP_MASK) { + fprintf(fd, " \n", xml_arch_names[arch]); + // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-oobe-protectyourpc + // It is really super insidous of Microsoft to call this option "ProtectYourPC", when it's really only about + // data collection. But of course, if it was called "AllowDataCollection", everyone would turn it off... + if (flags & UNATTEND_NO_DATA_COLLECTION) { + fprintf(fd, " \n"); + fprintf(fd, " 3\n"); + fprintf(fd, " \n"); + } + if (flags & UNATTEND_DUPLICATE_USER) { + order = 1; + char username[128] = { 0 }; + DWORD size = sizeof(username); + if (GetUserNameU(username, &size) && username[0] != 0) { + // If we create a local account in unattend.xml, then we can get Windows 11 + // 22H2 to skip MSA even if the network is connected during installation. + fprintf(fd, " \n"); + fprintf(fd, " \n"); + fprintf(fd, " \n"); + fprintf(fd, " %s\n", username); + fprintf(fd, " %s\n", username); + fprintf(fd, " Administrators;Power Users\n"); + // Sets an empty password for the account (which, in Microsoft's convoluted ways, + // needs to be initialized to the Base64 encoded UTF-16 string "Password"). + // The use of an empty password has both the advantage of not having to ask users + // to type in a password in Rufus (which they might be weary of) as well as allowing + // automated logon during setup. + fprintf(fd, " \n"); + fprintf(fd, " UABhAHMAcwB3AG8AcgBkAA==\n"); + fprintf(fd, " false</PlainText>\n"); + fprintf(fd, " </Password>\n"); + fprintf(fd, " </LocalAccount>\n"); + fprintf(fd, " </LocalAccounts>\n"); + fprintf(fd, " </UserAccounts>\n"); + // Since we set a blank password, we'll ask the user to change it at next logon. + fprintf(fd, " <FirstLogonCommands>\n"); + fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n"); + fprintf(fd, " <Order>%d</Order>\n", order++); + fprintf(fd, " <CommandLine>net user &quot;%s&quot; /logonpasswordchg:yes</CommandLine>\n", username); + fprintf(fd, " </SynchronousCommand>\n"); + fprintf(fd, " </FirstLogonCommands>\n"); + } else { + uprintf("Warning: Could not retreive current user name. Local Account was not created"); + } + } + fprintf(fd, " </component>\n"); + } + if (flags & UNATTEND_OOBE_INTERNATIONAL_MASK) { + fprintf(fd, " <component name=\"Microsoft-Windows-International-Core\" processorArchitecture=\"%s\" language=\"neutral\" " + "xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + "publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); + // What a frigging mess retreiving and trying to match the various locales + // Microsoft has made. And, *NO*, the new User Language Settings have not + // improved things in the slightest. They made it much worse for developers! + fprintf(fd, " <InputLocale>%s</InputLocale>\n", + ReadRegistryKeyStr(REGKEY_HKCU, "Keyboard Layout\\Preload\\1")); + fprintf(fd, " <SystemLocale>%s</SystemLocale>\n", ToLocaleName(GetSystemDefaultLCID())); + fprintf(fd, " <UserLocale>%s</UserLocale>\n", ToLocaleName(GetUserDefaultLCID())); + fprintf(fd, " <UILanguage>%s</UILanguage>\n", ToLocaleName(GetUserDefaultUILanguage())); + fprintf(fd, " <UILanguageFallback>%s</UILanguageFallback>\n", + // NB: Officially, this is a REG_MULTI_SZ string + ReadRegistryKeyStr(REGKEY_HKLM, "SYSTEM\\CurrentControlSet\\Control\\Nls\\Language\\InstallLanguageFallback")); + fprintf(fd, " </component>\n"); } - fprintf(fd, " </component>\n"); fprintf(fd, " </settings>\n"); } - if (mask & UNATTEND_OFFLINE_SERVICING) { + if (flags & UNATTEND_OFFLINE_SERVICING_MASK) { fprintf(fd, " <settings pass=\"offlineServicing\">\n"); - if (mask & UNATTEND_OFFLINE_INTERNAL_DRIVES) { + if (flags & UNATTEND_OFFLINE_INTERNAL_DRIVES) { fprintf(fd, " <component name=\"Microsoft-Windows-PartitionManager\" processorArchitecture=\"%s\" language=\"neutral\" " "xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " "publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]); @@ -1610,12 +1672,16 @@ static DWORD WINAPI BootCheckThread(LPVOID param) StrArrayCreate(&options, 2); if (img_report.win_version.build >= 22500) { StrArrayAdd(&options, lmprintf(MSG_330), TRUE); - MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT_MASK); + MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT); } StrArrayAdd(&options, lmprintf(MSG_331), TRUE); - MAP_BIT(UNATTEND_NO_DATA_COLLECTION_MASK); + MAP_BIT(UNATTEND_NO_DATA_COLLECTION); StrArrayAdd(&options, lmprintf(MSG_332), TRUE); MAP_BIT(UNATTEND_OFFLINE_INTERNAL_DRIVES); + StrArrayAdd(&options, lmprintf(MSG_333), TRUE); + MAP_BIT(UNATTEND_DUPLICATE_USER); + StrArrayAdd(&options, lmprintf(MSG_334), TRUE); + MAP_BIT(UNATTEND_DUPLICATE_LOCALE); i = SelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_326), lmprintf(MSG_327), options.String, options.Index, remap8(unattend_xml_mask, map, FALSE)); StrArrayDestroy(&options); @@ -1623,7 +1689,7 @@ static DWORD WINAPI BootCheckThread(LPVOID param) goto out; // Remap i to the correct bit positions before calling CreateUnattendXml() i = remap8(i, map, TRUE); - unattend_xml_path = CreateUnattendXml(arch, i); + unattend_xml_path = CreateUnattendXml(arch, i | UNATTEND_WINDOWS_TO_GO); // Keep the bits we didn't process unattend_xml_mask &= ~(remap8(0xff, map, TRUE)); // And add back the bits we did process @@ -1666,15 +1732,19 @@ static DWORD WINAPI BootCheckThread(LPVOID param) uint8_t map[8] = { 0 }, b = 1; StrArrayCreate(&options, 4); StrArrayAdd(&options, lmprintf(MSG_328), TRUE); - MAP_BIT(UNATTEND_SECUREBOOT_TPM_MASK); + MAP_BIT(UNATTEND_SECUREBOOT_TPM); StrArrayAdd(&options, lmprintf(MSG_329), TRUE); - MAP_BIT(UNATTEND_MINRAM_MINDISK_MASK); + MAP_BIT(UNATTEND_MINRAM_MINDISK); if (img_report.win_version.build >= 22500) { StrArrayAdd(&options, lmprintf(MSG_330), TRUE); - MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT_MASK); + MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT); } StrArrayAdd(&options, lmprintf(MSG_331), TRUE); - MAP_BIT(UNATTEND_NO_DATA_COLLECTION_MASK); + MAP_BIT(UNATTEND_NO_DATA_COLLECTION); + StrArrayAdd(&options, lmprintf(MSG_333), TRUE); + MAP_BIT(UNATTEND_DUPLICATE_USER); + StrArrayAdd(&options, lmprintf(MSG_334), TRUE); + MAP_BIT(UNATTEND_DUPLICATE_LOCALE); i = SelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_326), lmprintf(MSG_327), options.String, options.Index, remap8(unattend_xml_mask, map, FALSE)); StrArrayDestroy(&options); @@ -1683,7 +1753,6 @@ static DWORD WINAPI BootCheckThread(LPVOID param) i = remap8(i, map, TRUE); unattend_xml_path = CreateUnattendXml(arch, i); // Remember the user preferences for the current session. - // TODO: Do we want to save the current mask as a permanent setting? unattend_xml_mask &= ~(remap8(0xff, map, TRUE)); unattend_xml_mask |= i; WriteSetting32(SETTING_WUE_OPTIONS, (UNATTEND_DEFAULT_MASK << 16) | unattend_xml_mask); @@ -2070,7 +2139,7 @@ static void InitDialog(HWND hDlg) uprintf("Syslinux versions: %s%s, %s%s", embedded_sl_version_str[0], embedded_sl_version_ext[0], embedded_sl_version_str[1], embedded_sl_version_ext[1]); uprintf("Grub versions: %s, %s", GRUB4DOS_VERSION, GRUB2_PACKAGE_VERSION); - uprintf("System locale ID: 0x%04X (%s)", GetUserDefaultUILanguage(), GetCurrentMUI()); + uprintf("System locale ID: 0x%04X (%s)", GetUserDefaultUILanguage(), ToLocaleName(GetUserDefaultUILanguage())); ubflush(); if (selected_locale->ctrl_id & LOC_NEEDS_UPDATE) { uprintf("NOTE: The %s translation requires an update, but the current translator hasn't submitted " @@ -2756,7 +2825,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA fs_type = (int)ComboBox_GetCurItemData(hFileSystem); write_as_image = FALSE; write_as_esp = FALSE; - unattend_xml_selection = 0; + unattend_xml_flags = 0; // Disable all controls except Cancel EnableControls(FALSE, FALSE); FormatStatus = 0; diff --git a/src/rufus.h b/src/rufus.h index f2136061..a29e8721 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -498,20 +498,24 @@ enum ArchType { ARCH_MAX }; -// Windows User Experience (unattend.xml) options -#define UNATTEND_SECUREBOOT_TPM_MASK 0x00001 -#define UNATTEND_MINRAM_MINDISK_MASK 0x00002 -#define UNATTEND_NO_ONLINE_ACCOUNT_MASK 0x00004 -#define UNATTEND_NO_DATA_COLLECTION_MASK 0x00008 +// Windows User Experience (unattend.xml) flags and masks +#define UNATTEND_SECUREBOOT_TPM 0x00001 +#define UNATTEND_MINRAM_MINDISK 0x00002 +#define UNATTEND_NO_ONLINE_ACCOUNT 0x00004 +#define UNATTEND_NO_DATA_COLLECTION 0x00008 #define UNATTEND_OFFLINE_INTERNAL_DRIVES 0x00010 -#define UNATTEND_DEFAULT_MASK 0x0001F +#define UNATTEND_DUPLICATE_LOCALE 0x00020 +#define UNATTEND_DUPLICATE_USER 0x00040 +#define UNATTEND_DEFAULT_MASK 0x0007F #define UNATTEND_WINDOWS_TO_GO 0x10000 // Special flag for Windows To Go -#define UNATTEND_WINPE_SETUP_MASK (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_MINRAM_MINDISK_MASK) -#define UNATTEND_SPECIALIZE_DEPLOYMENT_MASK (UNATTEND_NO_ONLINE_ACCOUNT_MASK) -#define UNATTEND_OOBE_SHELL_SETUP (UNATTEND_NO_DATA_COLLECTION_MASK) -#define UNATTEND_OFFLINE_SERVICING (UNATTEND_OFFLINE_INTERNAL_DRIVES) -#define UNATTEND_DEFAULT_SELECTION (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_NO_ONLINE_ACCOUNT_MASK | UNATTEND_OFFLINE_INTERNAL_DRIVES) +#define UNATTEND_WINPE_SETUP_MASK (UNATTEND_SECUREBOOT_TPM | UNATTEND_MINRAM_MINDISK) +#define UNATTEND_SPECIALIZE_DEPLOYMENT_MASK (UNATTEND_NO_ONLINE_ACCOUNT) +#define UNATTEND_OOBE_SHELL_SETUP_MASK (UNATTEND_NO_DATA_COLLECTION | UNATTEND_DUPLICATE_USER) +#define UNATTEND_OOBE_INTERNATIONAL_MASK (UNATTEND_DUPLICATE_LOCALE) +#define UNATTEND_OOBE_MASK (UNATTEND_OOBE_SHELL_SETUP_MASK | UNATTEND_OOBE_INTERNATIONAL_MASK) +#define UNATTEND_OFFLINE_SERVICING_MASK (UNATTEND_OFFLINE_INTERNAL_DRIVES) +#define UNATTEND_DEFAULT_SELECTION_MASK (UNATTEND_SECUREBOOT_TPM | UNATTEND_NO_ONLINE_ACCOUNT | UNATTEND_OFFLINE_INTERNAL_DRIVES) /* * Globals @@ -652,7 +656,7 @@ extern BOOL IsBufferInDB(const unsigned char* buf, const size_t len); #define printbitslz(x) _printbits(sizeof(x), &x, 1) extern char* _printbits(size_t const size, void const * const ptr, int leading_zeroes); extern BOOL IsCurrentProcessElevated(void); -extern char* GetCurrentMUI(void); +extern char* ToLocaleName(DWORD lang_id); extern void SetAlertPromptMessages(void); extern BOOL SetAlertPromptHook(void); extern void ClrAlertPromptHook(void); diff --git a/src/rufus.rc b/src/rufus.rc index 5a3d1dd0..2a49c48e 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.20.1919" +CAPTION "Rufus 3.20.1920" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,20,1919,0 - PRODUCTVERSION 3,20,1919,0 + FILEVERSION 3,20,1920,0 + PRODUCTVERSION 3,20,1920,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.20.1919" + VALUE "FileVersion", "3.20.1920" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2022 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.20.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.20.1919" + VALUE "ProductVersion", "3.20.1920" END END BLOCK "VarFileInfo" diff --git a/src/stdfn.c b/src/stdfn.c index a2fa59ec..bce30a16 100644 --- a/src/stdfn.c +++ b/src/stdfn.c @@ -1017,12 +1017,12 @@ out: return r; } -char* GetCurrentMUI(void) +char* ToLocaleName(DWORD lang_id) { static char mui_str[LOCALE_NAME_MAX_LENGTH]; wchar_t wmui_str[LOCALE_NAME_MAX_LENGTH]; - if (LCIDToLocaleName(GetUserDefaultUILanguage(), wmui_str, LOCALE_NAME_MAX_LENGTH, 0) > 0) { + if (LCIDToLocaleName(lang_id, wmui_str, LOCALE_NAME_MAX_LENGTH, 0) > 0) { wchar_to_utf8_no_alloc(wmui_str, mui_str, LOCALE_NAME_MAX_LENGTH); } else { static_strcpy(mui_str, "en-US"); diff --git a/src/stdlg.c b/src/stdlg.c index 48660b93..1cc5fe13 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -1569,8 +1569,8 @@ void SetFidoCheck(void) // - Powershell being installed // - Rufus running in AppStore mode or update check being enabled // - URL for the script being reachable - if ((ReadRegistryKey32(REGKEY_HKLM, "Microsoft\\PowerShell\\1\\Install") <= 0) && - (ReadRegistryKey32(REGKEY_HKLM, "Microsoft\\PowerShell\\3\\Install") <= 0)) { + if ((ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\PowerShell\\1\\Install") <= 0) && + (ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\PowerShell\\3\\Install") <= 0)) { ubprintf("Notice: The ISO download feature has been deactivated because " "a compatible PowerShell version was not detected on this system."); return; @@ -2059,7 +2059,7 @@ void SetAlertPromptMessages(void) // Fetch the localized strings in the relevant MUI // Must use sysnative_dir rather than system_dir as we may not find the MUI's otherwise // Also don't bother with LibLibraryEx() since we have a full path here. - static_sprintf(mui_path, "%s\\%s\\shell32.dll.mui", sysnative_dir, GetCurrentMUI()); + static_sprintf(mui_path, "%s\\%s\\shell32.dll.mui", sysnative_dir, ToLocaleName(GetUserDefaultUILanguage())); hMui = LoadLibraryU(mui_path); if (hMui != NULL) { // 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text) diff --git a/src/vhd.c b/src/vhd.c index acf3148b..c6410191 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -167,8 +167,8 @@ static int _index, progress_op = OP_FILE_COPY, progress_msg = MSG_267; static BOOL Get7ZipPath(void) { - if ( (GetRegistryKeyStr(REGKEY_HKCU, "7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) - || (GetRegistryKeyStr(REGKEY_HKLM, "7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) ) { + if ( (GetRegistryKeyStr(REGKEY_HKCU, "Software\\7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) + || (GetRegistryKeyStr(REGKEY_HKLM, "Software\\7-Zip\\Path", sevenzip_path, sizeof(sevenzip_path))) ) { static_strcat(sevenzip_path, "\\7z.exe"); return (_accessU(sevenzip_path, 0) != -1); }