diff --git a/configure b/configure index ca98e468..4be0b97c 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for rufus 2.11. +# Generated by GNU Autoconf 2.69 for rufus 2.12. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='rufus' PACKAGE_TARNAME='rufus' -PACKAGE_VERSION='2.11' -PACKAGE_STRING='rufus 2.11' +PACKAGE_VERSION='2.12' +PACKAGE_STRING='rufus 2.12' PACKAGE_BUGREPORT='https://github.com/pbatard/rufus/issues' PACKAGE_URL='http://rufus.akeo.ie' @@ -1228,7 +1228,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures rufus 2.11 to adapt to many kinds of systems. +\`configure' configures rufus 2.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1294,7 +1294,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of rufus 2.11:";; + short | recursive ) echo "Configuration of rufus 2.12:";; esac cat <<\_ACEOF @@ -1385,7 +1385,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -rufus configure 2.11 +rufus configure 2.12 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1440,7 +1440,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by rufus $as_me 2.11, which was +It was created by rufus $as_me 2.12, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2303,7 +2303,7 @@ fi # Define the identity of the package. PACKAGE='rufus' - VERSION='2.11' + VERSION='2.12' cat >>confdefs.h <<_ACEOF @@ -4482,7 +4482,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by rufus $as_me 2.11, which was +This file was extended by rufus $as_me 2.12, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4536,7 +4536,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -rufus config.status 2.11 +rufus config.status 2.12 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 1a818a44..320b9fca 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([rufus], [2.11], [https://github.com/pbatard/rufus/issues], [rufus], [http://rufus.akeo.ie]) +AC_INIT([rufus], [2.12], [https://github.com/pbatard/rufus/issues], [rufus], [http://rufus.akeo.ie]) AM_INIT_AUTOMAKE([-Wno-portability foreign no-dist no-dependencies]) AC_CONFIG_SRCDIR([src/rufus.c]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/res/localization/rufus.loc b/res/localization/rufus.loc index cea0e23a..ef64a914 100644 --- a/res/localization/rufus.loc +++ b/res/localization/rufus.loc @@ -553,6 +553,8 @@ t MSG_287 "Detection of non-USB removable drives" t MSG_288 "Missing elevated privileges" t MSG_289 "This application can only run with elevated privileges" t MSG_290 "File Indexing" +t MSG_291 "Version selection" +t MSG_292 "Please select the version of Windows you want to install:" ################################################################################ ############################# TRANSLATOR END COPY ############################## diff --git a/src/dev.c b/src/dev.c index ef25037c..8c23bb87 100644 --- a/src/dev.c +++ b/src/dev.c @@ -313,7 +313,7 @@ BOOL GetDevices(DWORD devnum) StrArrayClear(&DriveLabel); StrArrayCreate(&dev_if_path, 128); // Add a dummy for string index zero, as this is what non matching hashes will point to - StrArrayAdd(&dev_if_path, ""); + StrArrayAdd(&dev_if_path, "", TRUE); device_id = (char*)malloc(MAX_PATH); if (device_id == NULL) @@ -340,7 +340,7 @@ BOOL GetDevices(DWORD devnum) // Find the Device IDs for all the children of this hub if (CM_Get_Child(&device_inst, dev_info_data.DevInst, 0) == CR_SUCCESS) { device_id[0] = 0; - s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath); + s = StrArrayAdd(&dev_if_path, devint_detail_data->DevicePath, TRUE); uuprintf(" Hub[%d] = '%s'", s, devint_detail_data->DevicePath); if ((s>= 0) && (CM_Get_Device_IDA(device_inst, device_id, MAX_PATH, 0) == CR_SUCCESS)) { if ((k = htab_hash(device_id, &htab_devid)) != 0) { @@ -741,8 +741,8 @@ BOOL GetDevices(DWORD devnum) } // Must ensure that the combo box is UNSORTED for indexes to be the same - StrArrayAdd(&DriveID, buffer); - StrArrayAdd(&DriveLabel, label); + StrArrayAdd(&DriveID, buffer, TRUE); + StrArrayAdd(&DriveLabel, label, TRUE); IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index)); maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry)); diff --git a/src/format.c b/src/format.c index 7861dc1f..a5516115 100644 --- a/src/format.c +++ b/src/format.c @@ -1277,8 +1277,11 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) static char san_policy_path[] = "?:\\san_policy.xml"; #endif static char unattend_path[] = "?:\\Windows\\System32\\sysprep\\unattend.xml"; + StrArray version_name, version_index; char *mounted_iso, *ms_efi = NULL, image[128], cmd[MAX_PATH]; + char tmp_path[MAX_PATH] = "", xml_file[MAX_PATH] = ""; unsigned char *buffer; + int i, index; wchar_t wVolumeName[] = L"?:"; DWORD bufsize; ULONG cluster_size; @@ -1305,11 +1308,50 @@ static BOOL SetupWinToGo(const char* drive_name, BOOL use_ms_efi) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); return FALSE; } + static_sprintf(image, "%s%s", mounted_iso, &img_report.install_wim_path[2]); uprintf("Mounted ISO as '%s'", mounted_iso); + // Then we need to take a look at the XML file in install.wim to allow users + // to select the version they want to extract + if ((GetTempPathU(sizeof(tmp_path), tmp_path) == 0) + || (GetTempFileNameU(tmp_path, APPLICATION_NAME, 0, xml_file) == 0) + || (xml_file[0] == 0)) { + // Last ditch effort to get a loc file - just extract it to the current directory + safe_strcpy(xml_file, sizeof(xml_file), ".\\RufVXml.tmp"); + } + // GetTempFileName() may leave a file behind + DeleteFileU(xml_file); + + // Must use the Windows WIM API as 7z messes up the XML + if (!WimExtractFile_API(image, 0, "[1].xml", xml_file)) { + uprintf("Failed to acquire WIM index"); + } + StrArrayCreate(&version_name, 16); + StrArrayCreate(&version_index, 16); + for (i = 0; (StrArrayAdd(&version_name, get_token_data_file_indexed("DISPLAYNAME", xml_file, i+1), FALSE) >= 0) && + (StrArrayAdd(&version_index, get_token_data_file_indexed("IMAGE INDEX", xml_file, i+1), FALSE) >= 0); i++); + DeleteFileU(xml_file); + + if (i > 1) + i = Selection(lmprintf(MSG_291), lmprintf(MSG_292), version_name.String, i); + if (i <= 0) { + uprintf("Cancelled by user"); + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED; + UnMountISO(); + StrArrayDestroy(&version_name); + StrArrayDestroy(&version_index); + return FALSE; + } else if (i == 0) { + index = 1; + } else { + index = atoi(version_index.String[i - 1]); + } + uprintf("Selected: '%s' (index %s)", version_name.String[i - 1], version_index.String[i - 1]); + StrArrayDestroy(&version_name); + StrArrayDestroy(&version_index); + // Now we use the WIM API to apply that image - static_sprintf(image, "%s%s", mounted_iso, &img_report.install_wim_path[2]); - if (!WimApplyImage(image, 1, drive_name)) { + if (!WimApplyImage(image, index, drive_name)) { uprintf("Failed to apply Windows To Go image"); if (!IS_ERROR(FormatStatus)) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_ISO_EXTRACT); diff --git a/src/iso.c b/src/iso.c index 5833dd21..9da84280 100644 --- a/src/iso.c +++ b/src/iso.c @@ -226,12 +226,12 @@ static BOOL check_iso_props(const char* psz_dirname, int64_t i_file_length, cons if (props->is_syslinux_cfg) { // Maintain a list of all the isolinux/syslinux configs identified so far - StrArrayAdd(&config_path, psz_fullpath); + StrArrayAdd(&config_path, psz_fullpath, TRUE); } for (i=0; i][ ]["]data["][ ][<] and is * modified by the parser */ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) { size_t i, r; BOOLEAN quoteth = FALSE; + BOOLEAN xml = FALSE; if ((wtoken == NULL) || (wline == NULL) || (wline[0] == 0)) return NULL; i = 0; - // Skip leading spaces + // Skip leading spaces and opening '<' + i += wcsspn(&wline[i], wspace); + if (wline[i] == L'<') + i++; i += wcsspn(&wline[i], wspace); // Our token should begin a line @@ -597,12 +601,14 @@ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) // Skip spaces i += wcsspn(&wline[i], wspace); - // Check for an equal sign - if (wline[i] != L'=') + // Check for '=' or '>' sign + if (wline[i] == L'>') + xml = TRUE; + else if (wline[i] != L'=') return NULL; i++; - // Skip spaces after equal sign + // Skip spaces i += wcsspn(&wline[i], wspace); // eliminate leading quote, if it exists @@ -615,7 +621,7 @@ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) r = i; // locate end of string or quote - while ( (wline[i] != 0) && ((wline[i] != L'"') || ((wline[i] == L'"') && (!quoteth))) ) + while ( (wline[i] != 0) && (((wline[i] != L'"') && (wline[i] != L'<')) || ((wline[i] == L'"') && (!quoteth)) || ((wline[i] == L'<') && (!xml))) ) i++; wline[i--] = 0; @@ -627,11 +633,12 @@ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) } /* - * Parse a file (ANSI or UTF-8 or UTF-16) and return the data for the first occurrence of 'token' + * Parse a file (ANSI or UTF-8 or UTF-16) and return the data for the 'index'th occurrence of 'token' * The returned string is UTF-8 and MUST be freed by the caller */ -char* get_token_data_file(const char* token, const char* filename) +char* get_token_data_file_indexed(const char* token, const char* filename, int index) { + int i = 0; wchar_t *wtoken = NULL, *wdata= NULL, *wfilename = NULL; wchar_t buf[1024]; FILE* fd = NULL; @@ -659,7 +666,7 @@ char* get_token_data_file(const char* token, const char* filename) // Ideally, we'd check that our buffer fits the line while (fgetws(buf, ARRAYSIZE(buf), fd) != NULL) { wdata = get_token_data_line(wtoken, buf); - if (wdata != NULL) { + if ((wdata != NULL) && (++i == index)) { ret = wchar_to_utf8(wdata); break; } diff --git a/src/resource.h b/src/resource.h index 1f1f9622..4991f722 100644 --- a/src/resource.h +++ b/src/resource.h @@ -134,6 +134,20 @@ #define IDC_SELECTION_LINE 1076 #define IDC_SELECTION_CHOICE1 1077 #define IDC_SELECTION_CHOICE2 1078 +#define IDC_SELECTION_CHOICE3 1079 +#define IDC_SELECTION_CHOICE4 1080 +#define IDC_SELECTION_CHOICE5 1081 +#define IDC_SELECTION_CHOICE6 1082 +#define IDC_SELECTION_CHOICE7 1083 +#define IDC_SELECTION_CHOICE8 1084 +#define IDC_SELECTION_CHOICE9 1085 +#define IDC_SELECTION_CHOICE10 1086 +#define IDC_SELECTION_CHOICE11 1087 +#define IDC_SELECTION_CHOICE12 1088 +#define IDC_SELECTION_CHOICE13 1089 +#define IDC_SELECTION_CHOICE14 1090 +#define IDC_SELECTION_CHOICE15 1091 +#define IDC_SELECTION_CHOICEMAX 1092 #define IDS_DEVICE_TXT 2000 #define IDS_PARTITION_TYPE_TXT 2001 #define IDS_FILESYSTEM_TXT 2002 diff --git a/src/rufus.c b/src/rufus.c index 4c1b7f81..1487e022 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -2108,6 +2108,10 @@ void SaveISO(void) } } +#ifdef RUFUS_TEST + extern int SelectionDyn(char* title, char* message, char** szChoice, int nChoices); +#endif + /* * Main dialog callback */ @@ -2140,6 +2144,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { + char* choices[] = { "Choice 1", "Choice 2", "Choice 3" }; + SelectionDyn("Test Choice", "Unused", choices, ARRAYSIZE(choices)); break; } #endif @@ -2438,8 +2444,9 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA (ComboBox_GetItemData(hBootType, ComboBox_GetCurSel(hBootType)) == BT_ISO)) { char* iso_image = lmprintf(MSG_036); char* dd_image = lmprintf(MSG_095); + char* choices[2] = { lmprintf(MSG_276, iso_image), lmprintf(MSG_277, dd_image) }; i = Selection(lmprintf(MSG_274), lmprintf(MSG_275, iso_image, dd_image, iso_image, dd_image), - lmprintf(MSG_276, iso_image), lmprintf(MSG_277, dd_image)); + choices, 2); if (i < 0) { // Cancel format_op_in_progress = FALSE; PROCESS_QUEUED_EVENTS; diff --git a/src/rufus.h b/src/rufus.h index a613d598..bd1ab458 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -422,7 +422,7 @@ extern BOOL CreateTooltip(HWND hControl, const char* message, int duration); extern void DestroyTooltip(HWND hWnd); extern void DestroyAllTooltips(void); extern BOOL Notification(int type, const notification_info* more_info, char* title, char* format, ...); -extern int Selection(char* title, char* message, char* selection1, char* selection2); +extern int Selection(char* title, char* message, char** choices, int size); extern SIZE GetTextSize(HWND hCtrl); extern BOOL ExtractDOS(const char* path); extern BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan); @@ -450,7 +450,8 @@ extern BOOL SetUpdateCheck(void); extern BOOL CheckForUpdates(BOOL force); extern void DownloadNewVersion(void); extern BOOL IsShown(HWND hDlg); -extern char* get_token_data_file(const char* token, const char* filename); +extern char* get_token_data_file_indexed(const char* token, const char* filename, int index); +#define get_token_data_file(token, filename) get_token_data_file_indexed(token, filename, 1) extern char* set_token_data_file(const char* token, const char* data, const char* filename); extern char* get_token_data_buffer(const char* token, unsigned int n, const char* buffer, size_t buffer_size); extern char* insert_section_data(const char* filename, const char* section, const char* data, BOOL dos2unix); @@ -459,6 +460,8 @@ extern char* replace_char(const char* src, const char c, const char* rep); extern void parse_update(char* buf, size_t len); extern uint8_t WimExtractCheck(void); extern BOOL WimExtractFile(const char* wim_image, int index, const char* src, const char* dst); +extern BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst); +extern BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst); extern BOOL WimApplyImage(const char* image, int index, const char* dst); extern BOOL IsBootableImage(const char* path); extern BOOL AppendVHDFooter(const char* vhd_path); @@ -507,7 +510,7 @@ typedef struct { uint32_t Max; // Maximum array size } StrArray; extern void StrArrayCreate(StrArray* arr, uint32_t initial_size); -extern int32_t StrArrayAdd(StrArray* arr, const char* str); +extern int32_t StrArrayAdd(StrArray* arr, const char* str, BOOL ); extern void StrArrayClear(StrArray* arr); extern void StrArrayDestroy(StrArray* arr); #define IsStrArrayEmpty(arr) (arr.Index == 0) diff --git a/src/rufus.rc b/src/rufus.rc index 2b8aacc5..adeb71cd 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 242, 376 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 2.11.1007" +CAPTION "Rufus 2.12.1008" FONT 8, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 @@ -144,10 +144,24 @@ BEGIN LTEXT "",IDC_STATIC,0,0,312,46 ICON IDI_ICON,IDC_SELECTION_ICON,6,6,20,20,0,WS_EX_TRANSPARENT LTEXT "Message",IDC_SELECTION_TEXT,35,5,269,8 - DEFPUSHBUTTON "Cancel",IDCANCEL,254,52,50,14 - PUSHBUTTON "OK",IDOK,196,52,50,14 - CONTROL "Choice 1",IDC_SELECTION_CHOICE1,"Button",BS_AUTORADIOBUTTON,35,18,269,10,WS_EX_TRANSPARENT + DEFPUSHBUTTON "OK",IDOK,196,52,50,14 + PUSHBUTTON "Cancel",IDCANCEL,254,52,50,14 + CONTROL "Choice 1",IDC_SELECTION_CHOICE1,"Button",BS_AUTORADIOBUTTON | WS_GROUP,35,18,269,10,WS_EX_TRANSPARENT CONTROL "Choice 2",IDC_SELECTION_CHOICE2,"Button",BS_AUTORADIOBUTTON,35,31,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 3",IDC_SELECTION_CHOICE3,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,44,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 4",IDC_SELECTION_CHOICE4,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,57,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 5",IDC_SELECTION_CHOICE5,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,70,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 6",IDC_SELECTION_CHOICE6,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,83,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 7",IDC_SELECTION_CHOICE7,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,96,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 8",IDC_SELECTION_CHOICE8,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,109,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 9",IDC_SELECTION_CHOICE9,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,122,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 10",IDC_SELECTION_CHOICE10,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,135,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 11",IDC_SELECTION_CHOICE11,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,148,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 12",IDC_SELECTION_CHOICE12,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,161,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 13",IDC_SELECTION_CHOICE13,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,174,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 14",IDC_SELECTION_CHOICE14,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,187,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 15",IDC_SELECTION_CHOICE15,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,200,269,10,WS_EX_TRANSPARENT + CONTROL "Choice 16",IDC_SELECTION_CHOICEMAX,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,35,21,269,10,WS_EX_TRANSPARENT END IDD_UPDATE_POLICY DIALOGEX 0, 0, 287, 198 @@ -320,8 +334,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,11,1007,0 - PRODUCTVERSION 2,11,1007,0 + FILEVERSION 2,12,1008,0 + PRODUCTVERSION 2,12,1008,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -338,13 +352,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "2.11.1007" + VALUE "FileVersion", "2.12.1008" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2016 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "2.11.1007" + VALUE "ProductVersion", "2.12.1008" END END BLOCK "VarFileInfo" diff --git a/src/stdfn.c b/src/stdfn.c index 2d45db56..8f4c3518 100644 --- a/src/stdfn.c +++ b/src/stdfn.c @@ -348,10 +348,10 @@ void StrArrayCreate(StrArray* arr, uint32_t initial_size) uprintf("Could not allocate string array\n"); } -int32_t StrArrayAdd(StrArray* arr, const char* str) +int32_t StrArrayAdd(StrArray* arr, const char* str, BOOL duplicate) { char** old_table; - if ((arr == NULL) || (arr->String == NULL)) + if ((arr == NULL) || (arr->String == NULL) || (str == NULL)) return -1; if (arr->Index == arr->Max) { arr->Max *= 2; @@ -363,7 +363,7 @@ int32_t StrArrayAdd(StrArray* arr, const char* str) return -1; } } - arr->String[arr->Index] = safe_strdup(str); + arr->String[arr->Index] = (duplicate)?safe_strdup(str):(char*)str; if (arr->String[arr->Index] == NULL) { uprintf("Could not store string in array\n"); return -1; diff --git a/src/stdlg.c b/src/stdlg.c index c5fa54fb..95073ead 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -54,7 +54,8 @@ PF_TYPE_DECL(WINAPI, LPITEMIDLIST, SHSimpleIDListFromPath, (PCWSTR pszPath)); static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE; static char* szMessageText = NULL; static char* szMessageTitle = NULL; -static char *szChoice1, *szChoice2; +static char **szChoice; +static int nChoices; static HWND hBrowseEdit; extern HWND hUpdatesDlg; static WNDPROC pOrgBrowseWndproc; @@ -68,6 +69,14 @@ static char *fp_title_str = "Microsoft Windows", *fp_button_str = "Format disk"; extern loc_cmd* selected_locale; +/* + * https://blogs.msdn.microsoft.com/oldnewthing/20040802-00/?p=38283/ + */ +void SetDialogFocus(HWND hDlg, HWND hCtrl) +{ + SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hCtrl, TRUE); +} + /* * We need a sub-callback to read the content of the edit box on exit and update * our path, else if what the user typed does match the selection, it is discarded. @@ -99,7 +108,7 @@ INT CALLBACK BrowseInfoCallback(HWND hDlg, UINT message, LPARAM lParam, LPARAM p // Get a handle to the edit control to fix that hBrowseEdit = FindWindowExA(hDlg, NULL, "Edit", NULL); SetWindowTextU(hBrowseEdit, szFolderPath); - SetFocus(hBrowseEdit); + SetDialogFocus(hDlg, hBrowseEdit); // On XP, BFFM_SETSELECTION can't be used with a Unicode Path in SendMessageW // or a pidl (at least with MinGW) => must use SendMessageA if (nWindowsVersion <= WINDOWS_XP) { @@ -849,6 +858,13 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA switch (message) { case WM_INITDIALOG: + // Don't overflow our max radio button + if (nChoices > (IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1)) { + uprintf("WARNING: Too many options requested for Selection (%d vs %d)", + nChoices, IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1); + nChoices = IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1; + } + // TODO: This shouldn't be needed when using DS_SHELLFONT // Get the system message box font. See http://stackoverflow.com/a/6057761 ncm.cbSize = sizeof(ncm); // If we're compiling with the Vista SDK or later, the NONCLIENTMETRICS struct @@ -865,8 +881,8 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA // Set the dialog to use the system message box font SendMessage(hDlg, WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); SendMessage(GetDlgItem(hDlg, IDC_SELECTION_TEXT), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); - SendMessage(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); - SendMessage(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); + for (i = 0; i < nChoices; i++) + SendMessage(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); SendMessage(GetDlgItem(hDlg, IDYES), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); SendMessage(GetDlgItem(hDlg, IDNO), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0)); @@ -880,9 +896,10 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA SetWindowTextU(hDlg, szMessageTitle); SetWindowTextU(GetDlgItem(hDlg, IDCANCEL), lmprintf(MSG_007)); SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_TEXT), szMessageText); - SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), szChoice1); - SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), szChoice2); - + for (i = 0; i < nChoices; i++) { + SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), szChoice[i]); + ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), SW_SHOW); + } // Move/Resize the controls as needed to fit our text hCtrl = GetDlgItem(hDlg, IDC_SELECTION_TEXT); hDC = GetDC(hCtrl); @@ -893,13 +910,16 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA dh = rect.bottom - rect.top - dh; if (hDC != NULL) ReleaseDC(hCtrl, hDC); - ResizeMoveCtrl(hDlg, hCtrl, 0, 0, 0, dh, 1.0f); + for (i = 0; i < nChoices; i++) + ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), 0, dh, 0, 0, 1.0f); + if (nChoices > 2) { + GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), &rect); + dh += (nChoices - 1) * (rect.bottom - rect.top) - 5; + } ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, dh, 1.0f); ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, -1), 0, 0, 0, dh, 1.0f); // IDC_STATIC = -1 ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_LINE), 0, dh, 0, 0, 1.0f); - ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), 0, dh, 0, 0, 1.0f); - ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), 0, dh, 0, 0, 1.0f); ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDOK), 0, dh, 0, 0, 1.0f); ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDCANCEL), 0, dh, 0, 0, 1.0f); @@ -926,10 +946,10 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: - if (Button_GetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2)) == BST_CHECKED) - r = 2; - else - r = 1; + for (i = 0; (i < nChoices) && + (Button_GetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i)) != BST_CHECKED); i++); + if (i < nChoices) + r = i + 1; // Fall through case IDNO: case IDCANCEL: @@ -944,15 +964,15 @@ INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARA /* * Display a selection question */ -int Selection(char* title, char* message, char* choice1, char* choice2) +int Selection(char* title, char* message, char** choices, int size) { int ret; dialog_showing++; szMessageTitle = title; szMessageText = message; - szChoice1 = choice1; - szChoice2 = choice2; + szChoice = choices; + nChoices = size; ret = (int)MyDialogBox(hMainInstance, IDD_SELECTION, hMainDialog, SelectionCallback); dialog_showing--; @@ -1819,3 +1839,134 @@ void ClrFormatPromptHook(void) { UnhookWinEvent(fp_weh); fp_weh = NULL; } + +#ifdef RUFUS_TEST +static inline LPWORD lpwAlign(LPWORD addr) +{ + return (LPWORD)((((uintptr_t)addr) + 3) & (~3)); +} + +INT_PTR CALLBACK SelectionDynCallback(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int r = -1; + switch (message) { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + r = 0; + case IDCANCEL: + EndDialog(hwndDlg, r); + return (INT_PTR)TRUE; + } + } + return FALSE; +} + +int SelectionDyn(char* title, char* message, char** szChoice, int nChoices) +{ +#define ID_RADIO 12345 + LPCWSTR lpwszTypeFace = L"MS Shell Dlg"; + LPDLGTEMPLATEA lpdt; + LPDLGITEMTEMPLATEA lpdit; + LPCWSTR lpwszCaption; + LPWORD lpw; + LPWSTR lpwsz; + int i, ret, nchar; + + lpdt = (LPDLGTEMPLATE)calloc(512 + nChoices * 256, 1); + + // Set up a dialog window + lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION | DS_MODALFRAME | DS_CENTER | DS_SHELLFONT; + lpdt->cdit = 2 + nChoices; + lpdt->x = 10; + lpdt->y = 10; + lpdt->cx = 300; + lpdt->cy = 100; + + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms645394.aspx: + // In a standard template for a dialog box, the DLGTEMPLATE structure is always immediately followed by + // three variable-length arrays that specify the menu, class, and title for the dialog box. + // When the DS_SETFONT style is specified, these arrays are also followed by a 16-bit value specifying + // point size and another variable-length array specifying a typeface name. Each array consists of one + // or more 16-bit elements. The menu, class, title, and font arrays must be aligned on WORD boundaries. + lpw = (LPWORD)(&lpdt[1]); + *lpw++ = 0; // No menu + *lpw++ = 0; // Default dialog class + lpwsz = (LPWSTR)lpw; + nchar = MultiByteToWideChar(CP_UTF8, 0, title, -1, lpwsz, 50); + lpw += nchar; + + // Set point size and typeface name if required + if (lpdt->style & (DS_SETFONT | DS_SHELLFONT)) { + *lpw++ = 8; + for (lpwsz = (LPWSTR)lpw, lpwszCaption = lpwszTypeFace; (*lpwsz++ = *lpwszCaption++) != 0; ); + lpw = (LPWORD)lpwsz; + } + + // Add an OK button + lpw = lpwAlign(lpw); + lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON; + lpdit->x = 10; + lpdit->y = 70; + lpdit->cx = 50; + lpdit->cy = 14; + lpdit->id = IDOK; + + lpw = (LPWORD)(&lpdit[1]); + *lpw++ = 0xFFFF; + *lpw++ = 0x0080; // Button class + + lpwsz = (LPWSTR)lpw; + nchar = MultiByteToWideChar(CP_UTF8, 0, "OK", -1, lpwsz, 50); + lpw += nchar; + *lpw++ = 0; // No creation data + + // Add a Cancel button + lpw = lpwAlign(lpw); + lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->x = 90; + lpdit->y = 70; + lpdit->cx = 50; + lpdit->cy = 14; + lpdit->id = IDCANCEL; + lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON; + + lpw = (LPWORD)(&lpdit[1]); + *lpw++ = 0xFFFF; + *lpw++ = 0x0080; + + lpwsz = (LPWSTR)lpw; + nchar = MultiByteToWideChar(CP_UTF8, 0, lmprintf(MSG_007), -1, lpwsz, 50); + lpw += nchar; + *lpw++ = 0; + + // Add radio buttons + for (i = 0; i < nChoices; i++) { + lpw = lpwAlign(lpw); + lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->x = 10; + lpdit->y = 10 + 15 * i; + lpdit->cx = 40; + lpdit->cy = 20; + lpdit->id = ID_RADIO; + lpdit->style = WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | (i == 0 ? WS_GROUP : 0); + + lpw = (LPWORD)(&lpdit[1]); + *lpw++ = 0xFFFF; + *lpw++ = 0x0080; + + lpwsz = (LPWSTR)lpw; + nchar = MultiByteToWideChar(CP_UTF8, 0, szChoice[i], -1, lpwsz, 150); + lpw += nchar; + *lpw++ = 0; + } + + ret = (int)DialogBoxIndirect(hMainInstance, (LPDLGTEMPLATE)lpdt, hMainDialog, (DLGPROC)SelectionDynCallback); + free(lpdt); + return ret; +} +#endif diff --git a/src/vhd.c b/src/vhd.c index 3ae54dcc..c5e977f8 100644 --- a/src/vhd.c +++ b/src/vhd.c @@ -93,6 +93,7 @@ PF_TYPE_DECL(WINAPI, BOOL, WIMSetTemporaryPath, (HANDLE, PWSTR)); PF_TYPE_DECL(WINAPI, HANDLE, WIMLoadImage, (HANDLE, DWORD)); PF_TYPE_DECL(WINAPI, BOOL, WIMApplyImage, (HANDLE, PCWSTR, DWORD)); PF_TYPE_DECL(WINAPI, BOOL, WIMExtractImagePath, (HANDLE, PWSTR, PWSTR, DWORD)); +PF_TYPE_DECL(WINAPI, BOOL, WIMGetImageInformation, (HANDLE, PVOID, PDWORD)); PF_TYPE_DECL(WINAPI, BOOL, WIMCloseHandle, (HANDLE)); PF_TYPE_DECL(WINAPI, DWORD, WIMRegisterMessageCallback, (HANDLE, FARPROC, PVOID)); PF_TYPE_DECL(WINAPI, DWORD, WIMUnregisterMessageCallback, (HANDLE, FARPROC)); @@ -345,6 +346,7 @@ uint8_t WimExtractCheck(void) PF_INIT(WIMLoadImage, Wimgapi); PF_INIT(WIMApplyImage, Wimgapi); PF_INIT(WIMExtractImagePath, Wimgapi); + PF_INIT(WIMGetImageInformation, Wimgapi); PF_INIT(WIMRegisterMessageCallback, Wimgapi); PF_INIT(WIMUnregisterMessageCallback, Wimgapi); PF_INIT(WIMCloseHandle, Wimgapi); @@ -368,16 +370,19 @@ uint8_t WimExtractCheck(void) // Extract a file from a WIM image using wimgapi.dll (Windows 7 or later) // NB: if you want progress from a WIM callback, you must run the WIM API call in its own thread // (which we don't do here) as it won't work otherwise. Thanks go to Erwan for figuring this out! -static BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst) +BOOL WimExtractFile_API(const char* image, int index, const char* src, const char* dst) { + static char* index_name = "[1].xml"; BOOL r = FALSE; DWORD dw = 0; HANDLE hWim = NULL; HANDLE hImage = NULL; + HANDLE hFile = NULL; wchar_t wtemp[MAX_PATH] = {0}; wchar_t* wimage = utf8_to_wchar(image); wchar_t* wsrc = utf8_to_wchar(src); wchar_t* wdst = utf8_to_wchar(dst); + char* wim_info; PF_INIT_OR_OUT(WIMCreateFile, Wimgapi); PF_INIT_OR_OUT(WIMSetTemporaryPath, Wimgapi); @@ -402,19 +407,30 @@ static BOOL WimExtractFile_API(const char* image, int index, const char* src, co goto out; } - hImage = pfWIMLoadImage(hWim, (DWORD)index); - if (hImage == NULL) { - uprintf(" Could not set index: %s", WindowsErrorString()); - goto out; - } - uprintf("Extracting: %s (From %s)", dst, src); - if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) { - uprintf(" Could not extract file: %s", WindowsErrorString()); - goto out; + if (safe_strcmp(src, index_name) == 0) { + if (!pfWIMGetImageInformation(hWim, &wim_info, &dw)) { + uprintf(" Could not access WIM info: %s", WindowsErrorString()); + goto out; + } + hFile = CreateFileW(wdst, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if ((hFile == INVALID_HANDLE_VALUE) || (!WriteFile(hFile, wim_info, dw, &dw, NULL))) { + uprintf(" Could not extract file: %s", WindowsErrorString()); + goto out; + } + } else { + hImage = pfWIMLoadImage(hWim, (DWORD)index); + if (hImage == NULL) { + uprintf(" Could not set index: %s", WindowsErrorString()); + goto out; + } + if (!pfWIMExtractImagePath(hImage, wsrc, wdst, 0)) { + uprintf(" Could not extract file: %s", WindowsErrorString()); + goto out; + } } r = TRUE; - UpdateProgress(OP_FINALIZE, -1.0f); out: if ((hImage != NULL) || (hWim != NULL)) { @@ -422,6 +438,7 @@ out: if (hImage != NULL) pfWIMCloseHandle(hImage); if (hWim != NULL) pfWIMCloseHandle(hWim); } + safe_closehandle(hFile); safe_free(wimage); safe_free(wsrc); safe_free(wdst); @@ -429,7 +446,7 @@ out: } // Extract a file from a WIM image using 7-Zip -static BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst) +BOOL WimExtractFile_7z(const char* image, int index, const char* src, const char* dst) { int n; size_t i; @@ -455,10 +472,7 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con // to issue 2 passes. See github issue #680. for (n = 0; n < 2; n++) { safe_strcpy(tmpdst, sizeof(tmpdst), dst); - for (i = strlen(tmpdst) - 1; i > 0; i--) { - if (tmpdst[i] == '\\') - break; - } + for (i = strlen(tmpdst) - 1; (i > 0) && (tmpdst[i] != '\\') && (tmpdst[i] != '/'); i--); tmpdst[i] = 0; safe_sprintf(cmdline, sizeof(cmdline), "\"%s\" -y e \"%s\" %s%s", sevenzip_path, @@ -468,7 +482,10 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con return FALSE; } - safe_strcat(tmpdst, sizeof(tmpdst), "\\bootmgfw.efi"); + for (i = safe_strlen(src); (i > 0) && (src[i] != '\\') && (src[i] != '/'); i--); + if (i == 0) + safe_strcat(tmpdst, sizeof(tmpdst), "\\"); + safe_strcat(tmpdst, sizeof(tmpdst), &src[i]); if (_access(tmpdst, 0) == 0) // File was extracted => move on break; @@ -481,7 +498,7 @@ static BOOL WimExtractFile_7z(const char* image, int index, const char* src, con // coverity[toctou] if (rename(tmpdst, dst) != 0) { - uprintf(" Could not rename %s to %s", tmpdst, dst); + uprintf(" Could not rename %s to %s: errno %d", tmpdst, dst, errno); return FALSE; }