mirror of
				https://github.com/pbatard/rufus.git
				synced 2024-08-14 23:57:05 +00:00 
			
		
		
		
	[core] add a cheat mode (Alt-P) to toggle GPT ESP to Basic Data
* Windows 10 or later *ONLY* * Also don't report a GPT partition name if it's empty
This commit is contained in:
		
							parent
							
								
									22fb8febc5
								
							
						
					
					
						commit
						23057f6124
					
				
					 6 changed files with 205 additions and 16 deletions
				
			
		
							
								
								
									
										174
									
								
								src/drive.c
									
										
									
									
									
								
							
							
						
						
									
										174
									
								
								src/drive.c
									
										
									
									
									
								
							|  | @ -34,6 +34,7 @@ | |||
| #include "rufus.h" | ||||
| #include "missing.h" | ||||
| #include "resource.h" | ||||
| #include "settings.h" | ||||
| #include "msapi_utf8.h" | ||||
| #include "localization.h" | ||||
| 
 | ||||
|  | @ -917,7 +918,7 @@ UINT GetDriveTypeFromIndex(DWORD DriveIndex) | |||
| char GetUnusedDriveLetter(void) | ||||
| { | ||||
| 	DWORD size; | ||||
| 	char drive_letter = 'Z'+1, *drive, drives[26*4 + 1];	/* "D:\", "E:\", etc., plus one NUL */ | ||||
| 	char drive_letter, *drive, drives[26*4 + 1];	/* "D:\", "E:\", etc., plus one NUL */ | ||||
| 
 | ||||
| 	size = GetLogicalDriveStringsA(sizeof(drives), drives); | ||||
| 	if (size == 0) { | ||||
|  | @ -944,6 +945,29 @@ out: | |||
| 	return (drive_letter > 'Z') ? 0 : drive_letter; | ||||
| } | ||||
| 
 | ||||
| BOOL IsDriveLetterInUse(const char drive_letter) | ||||
| { | ||||
| 	DWORD size; | ||||
| 	char *drive, drives[26 * 4 + 1]; | ||||
| 
 | ||||
| 	size = GetLogicalDriveStringsA(sizeof(drives), drives); | ||||
| 	if (size == 0) { | ||||
| 		uprintf("GetLogicalDriveStrings failed: %s", WindowsErrorString()); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 	if (size > sizeof(drives)) { | ||||
| 		uprintf("GetLogicalDriveStrings: Buffer too small (required %d vs. %d)", size, sizeof(drives)); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 
 | ||||
| 	for (drive = drives; *drive; drive += safe_strlen(drive) + 1) { | ||||
| 		if (drive_letter == (char)toupper((int)*drive)) | ||||
| 			return TRUE; | ||||
| 	} | ||||
| 
 | ||||
| 	return FALSE; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return the drive letter and volume label | ||||
|  * If the drive doesn't have a volume assigned, space is returned for the letter | ||||
|  | @ -1130,6 +1154,142 @@ BOOL AnalyzePBR(HANDLE hLogicalVolume) | |||
| 	return TRUE; | ||||
| } | ||||
| 
 | ||||
| static BOOL StoreEspInfo(GUID* guid) | ||||
| { | ||||
| 	uint8_t j; | ||||
| 	char key_name[2][16], *str; | ||||
| 	// Look for an empty slot and use that if available
 | ||||
| 	for (j = 1; j <= MAX_ESP_TOGGLE; j++) { | ||||
| 		static_sprintf(key_name[0], "ToggleEsp%02u", j); | ||||
| 		str = ReadSettingStr(key_name[0]); | ||||
| 		if ((str == NULL) || (str[0] == 0)) | ||||
| 			return WriteSettingStr(key_name[0], GuidToString(guid)); | ||||
| 	} | ||||
| 	// All slots are used => Move every key down and add to last slot
 | ||||
| 	// NB: No, we don't care that the slot we remove may not be the oldest.
 | ||||
| 	for (j = 1; j < MAX_ESP_TOGGLE; j++) { | ||||
| 		static_sprintf(key_name[0], "ToggleEsp%02u", j); | ||||
| 		static_sprintf(key_name[1], "ToggleEsp%02u", j + 1); | ||||
| 		WriteSettingStr(key_name[0], ReadSettingStr(key_name[1])); | ||||
| 	} | ||||
| 	return WriteSettingStr(key_name[1], GuidToString(guid)); | ||||
| } | ||||
| 
 | ||||
| static GUID* GetEspGuid(uint8_t index) | ||||
| { | ||||
| 	char key_name[16]; | ||||
| 
 | ||||
| 	static_sprintf(key_name, "ToggleEsp%02u", index); | ||||
| 	return StringToGuid(ReadSettingStr(key_name)); | ||||
| } | ||||
| 
 | ||||
| static BOOL ClearEspInfo(uint8_t index) | ||||
| { | ||||
| 	char key_name[16]; | ||||
| 	static_sprintf(key_name, "ToggleEsp%02u", index); | ||||
| 	return WriteSettingStr(key_name, ""); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * This calls changes the type of a GPT ESP back and forth to Basic Data. | ||||
|  * Needed because Windows 10 doesn't mount ESPs by default, and also | ||||
|  * doesn't let usermode apps (such as File Explorer) access mounted ESPs. | ||||
|  */ | ||||
| BOOL ToggleEsp(DWORD DriveIndex) | ||||
| { | ||||
| 	char *volume_name, mount_point[] = DEFAULT_ESP_MOUNT_POINT; | ||||
| 	BOOL r, ret = FALSE, found = FALSE; | ||||
| 	HANDLE hPhysical; | ||||
| 	DWORD size, i, j, esp_index = 0; | ||||
| 	BYTE layout[4096] = { 0 }; | ||||
| 	GUID* guid; | ||||
| 	PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | ||||
| 
 | ||||
| 	if (nWindowsVersion < WINDOWS_10) { | ||||
| 		uprintf("ESP toggling is only available for Windows 10 or later"); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 
 | ||||
| 	hPhysical = GetPhysicalHandle(DriveIndex, FALSE, TRUE, TRUE); | ||||
| 	if (hPhysical == INVALID_HANDLE_VALUE) | ||||
| 		return FALSE; | ||||
| 
 | ||||
| 	r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, | ||||
| 		NULL, 0, layout, sizeof(layout), &size, NULL); | ||||
| 	if (!r || size <= 0) { | ||||
| 		uprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (DriveLayout->PartitionStyle != PARTITION_STYLE_GPT) { | ||||
| 		uprintf("ESP toggling is only available for GPT drives"); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	// See if the current drive contains an ESP
 | ||||
| 	for (i = 0, j = 0; i < DriveLayout->PartitionCount; i++) { | ||||
| 		if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) { | ||||
| 			esp_index = i; | ||||
| 			j++; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (j > 1) { | ||||
| 		uprintf("ESP toggling is not available for drives with more than one ESP"); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (j == 1) { | ||||
| 		// ESP -> Basic Data
 | ||||
| 		i = esp_index; | ||||
| 		uprintf("ESP name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); | ||||
| 		if (!StoreEspInfo(&DriveLayout->PartitionEntry[i].Gpt.PartitionId)) { | ||||
| 			uprintf("ESP toggling data could not be stored"); | ||||
| 			goto out; | ||||
| 		} | ||||
| 		DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_MICROSOFT_DATA; | ||||
| 	} else { | ||||
| 		// Basic Data -> ESP
 | ||||
| 		for (j = 1; j <= MAX_ESP_TOGGLE; j++) { | ||||
| 			guid = GetEspGuid((uint8_t)j); | ||||
| 			if (guid != NULL) { | ||||
| 				for (i = 0; i < DriveLayout->PartitionCount; i++) { | ||||
| 					if (CompareGUID(guid, &DriveLayout->PartitionEntry[i].Gpt.PartitionId)) { | ||||
| 						uprintf("BD name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); | ||||
| 						found = TRUE; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if (found) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (j > MAX_ESP_TOGGLE) | ||||
| 			goto out; | ||||
| 		DriveLayout->PartitionEntry[i].Gpt.PartitionType = PARTITION_GENERIC_ESP; | ||||
| 	} | ||||
| 
 | ||||
| 	DriveLayout->PartitionEntry[i].RewritePartition = TRUE;	// Just in case
 | ||||
| 	r = DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, (BYTE*)DriveLayout, size, NULL, 0, &size, NULL); | ||||
| 	if (!r) { | ||||
| 		uprintf("Could not set drive layout: %s", WindowsErrorString()); | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	RefreshDriveLayout(hPhysical); | ||||
| 	if (CompareGUID(&DriveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_GENERIC_ESP)) { | ||||
| 		// We successfully reverted ESP from Basic Data -> Delete stored ESP info
 | ||||
| 		ClearEspInfo((uint8_t)j); | ||||
| 	} else if (!IsDriveLetterInUse(*mount_point)) { | ||||
| 		// We succesfully switched ESP to Basic Data -> Try to mount it
 | ||||
| 		volume_name = GetLogicalName(DriveIndex, DriveLayout->PartitionEntry[i].StartingOffset.QuadPart, TRUE, FALSE); | ||||
| 		MountVolume(mount_point, volume_name); | ||||
| 		free(volume_name); | ||||
| 	} | ||||
| 	ret = TRUE; | ||||
| 
 | ||||
| out: | ||||
| 	safe_closehandle(hPhysical); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Fill the drive properties (size, FS, etc) | ||||
|  * Returns TRUE if the drive has a partition that can be mounted in Windows, FALSE otherwise | ||||
|  | @ -1162,14 +1322,14 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys | |||
| 
 | ||||
| 	hPhysical = GetPhysicalHandle(DriveIndex, FALSE, FALSE, TRUE); | ||||
| 	if (hPhysical == INVALID_HANDLE_VALUE) | ||||
| 		return 0; | ||||
| 		return FALSE; | ||||
| 
 | ||||
| 	r = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, | ||||
| 			NULL, 0, geometry, sizeof(geometry), &size, NULL); | ||||
| 	if (!r || size <= 0) { | ||||
| 		suprintf("Could not get geometry for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); | ||||
| 		safe_closehandle(hPhysical); | ||||
| 		return 0; | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 	SelectedDrive.DiskSize = DiskGeometry->DiskSize.QuadPart; | ||||
| 	SelectedDrive.SectorSize = DiskGeometry->Geometry.BytesPerSector; | ||||
|  | @ -1192,7 +1352,7 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys | |||
| 	if (!r || size <= 0) { | ||||
| 		suprintf("Could not get layout for drive 0x%02x: %s", DriveIndex, WindowsErrorString()); | ||||
| 		safe_closehandle(hPhysical); | ||||
| 		return 0; | ||||
| 		return FALSE; | ||||
| 	} | ||||
| 
 | ||||
| #if defined(__GNUC__) | ||||
|  | @ -1282,8 +1442,10 @@ BOOL GetDrivePartitionData(DWORD DriveIndex, char* FileSystemName, DWORD FileSys | |||
| 			} | ||||
| 			SelectedDrive.nPartitions++; | ||||
| 			isUefiNtfs = (wcscmp(DriveLayout->PartitionEntry[i].Gpt.Name, L"UEFI:NTFS") == 0); | ||||
| 			suprintf("Partition %d%s:\r\n  Type: %s\r\n  Name: '%S'", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "", | ||||
| 				GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType), DriveLayout->PartitionEntry[i].Gpt.Name); | ||||
| 			suprintf("Partition %d%s:\r\n  Type: %s", i+1, isUefiNtfs ? " (UEFI:NTFS)" : "", | ||||
| 				GetGPTPartitionType(&DriveLayout->PartitionEntry[i].Gpt.PartitionType)); | ||||
| 			if (DriveLayout->PartitionEntry[i].Gpt.Name[0] != 0) | ||||
| 				suprintf("  Name: '%S'", DriveLayout->PartitionEntry[i].Gpt.Name); | ||||
| 			suprintf("  ID: %s\r\n  Size: %s (%" PRIi64 " bytes)\r\n  Start Sector: %" PRIi64 ", Attributes: 0x%016" PRIX64, | ||||
| 				GuidToString(&DriveLayout->PartitionEntry[i].Gpt.PartitionId), | ||||
| 				SizeToHumanReadable(DriveLayout->PartitionEntry[i].PartitionLength.QuadPart, TRUE, FALSE), | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| /*
 | ||||
|  * Rufus: The Reliable USB Formatting Utility | ||||
|  * Drive access function calls | ||||
|  * Copyright © 2011-2019 Pete Batard <pete@akeo.ie> | ||||
|  * Copyright © 2011-2020 Pete Batard <pete@akeo.ie> | ||||
|  * | ||||
|  * 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 | ||||
|  | @ -377,6 +377,7 @@ int GetDriveNumber(HANDLE hDrive, char* path); | |||
| BOOL GetDriveLetters(DWORD DriveIndex, char* drive_letters); | ||||
| UINT GetDriveTypeFromIndex(DWORD DriveIndex); | ||||
| char GetUnusedDriveLetter(void); | ||||
| BOOL IsDriveLetterInUse(const char drive_letter); | ||||
| BOOL GetDriveLabel(DWORD DriveIndex, char* letter, char** label); | ||||
| uint64_t GetDriveSize(DWORD DriveIndex); | ||||
| BOOL IsMediaPresent(DWORD DriveIndex); | ||||
|  | @ -399,3 +400,4 @@ BOOL CyclePort(int index); | |||
| int CycleDevice(int index); | ||||
| BOOL RefreshLayout(DWORD DriveIndex); | ||||
| BOOL GetOpticalMedia(IMG_SAVE* img_save); | ||||
| BOOL ToggleEsp(DWORD DriveIndex); | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/rufus.c
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/rufus.c
									
										
									
									
									
								
							|  | @ -3286,7 +3286,7 @@ relaunch: | |||
| 	while(GetMessage(&msg, NULL, 0, 0)) { | ||||
| 		static BOOL ctrl_without_focus = FALSE; | ||||
| 		BOOL no_focus = (msg.message == WM_SYSKEYDOWN) && !(msg.lParam & 0x20000000); | ||||
| 		// ** ****** **** ** **********
 | ||||
| 		// ** ****** **** *************
 | ||||
| 		// .,ABCDEFGHIJKLMNOPQRSTUVWXYZ
 | ||||
| 
 | ||||
| 		// Sigh... The things one need to do to detect standalone use of the 'Alt' key.
 | ||||
|  | @ -3452,6 +3452,14 @@ relaunch: | |||
| 				SaveISO(); | ||||
| 				continue; | ||||
| 			} | ||||
| 			// Alt-P => Toggle GPT ESP to and from Basic Data type (Windows 10 or later)
 | ||||
| 			if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'P')) { | ||||
| 				int index = ComboBox_GetCurSel(hDeviceList); | ||||
| 				DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, index); | ||||
| 				if (ToggleEsp(DeviceNum)) | ||||
| 					CyclePort(index); | ||||
| 				continue; | ||||
| 			} | ||||
| 			// Alt-Q => Enable file indexing (for file systems that support it)
 | ||||
| 			// For multiple reasons, file indexing is disabled by default
 | ||||
| 			if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Q')) { | ||||
|  |  | |||
|  | @ -71,6 +71,7 @@ | |||
| #define MAX_REFRESH                 25			// How long we should wait to refresh UI elements (in ms)
 | ||||
| #define MAX_GUID_STRING_LENGTH      40 | ||||
| #define MAX_PARTITIONS              16			// Maximum number of partitions we handle
 | ||||
| #define MAX_ESP_TOGGLE              8			// Maximum number of entries we record to toggle GPT ESP back and forth
 | ||||
| #define MAX_SECTORS_TO_CLEAR        128			// nb sectors to zap when clearing the MBR/GPT (must be >34)
 | ||||
| #define MAX_WININST                 4			// Max number of install[.wim|.esd] we can handle on an image
 | ||||
| #define MBR_UEFI_MARKER             0x49464555	// 'U', 'E', 'F', 'I', as a 32 bit little endian longword
 | ||||
|  | @ -111,6 +112,7 @@ | |||
| #define WPPRECORDER_MORE_INFO_URL   "https://github.com/pbatard/rufus/wiki/FAQ#BSODs_with_Windows_To_Go_drives_created_from_Windows_10_1809_ISOs"
 | ||||
| #define SEVENZIP_URL                "https://www.7-zip.org"
 | ||||
| #define FILES_DIR                   "rufus_files" | ||||
| #define DEFAULT_ESP_MOUNT_POINT     "S:\\" | ||||
| #define IS_POWER_OF_2(x)            ((x != 0) && (((x) & ((x) - 1)) == 0)) | ||||
| #define IGNORE_RETVAL(expr)         do { (void)(expr); } while(0) | ||||
| #ifndef ARRAYSIZE | ||||
|  | @ -485,6 +487,7 @@ extern void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t | |||
| #define UpdateProgressWithInfoInit(hProgressDialog, bNoAltMode) UpdateProgressWithInfo(OP_INIT, (int)bNoAltMode, (uint64_t)(uintptr_t)hProgressDialog, 0); | ||||
| extern const char* StrError(DWORD error_code, BOOL use_default_locale); | ||||
| extern char* GuidToString(const GUID* guid); | ||||
| extern GUID* StringToGuid(const char* str); | ||||
| extern char* SizeToHumanReadable(uint64_t size, BOOL copy_to_log, BOOL fake_units); | ||||
| extern char* TimestampToHumanReadable(uint64_t ts); | ||||
| extern HWND MyCreateDialog(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC lpDialogFunc); | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								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.11.1667" | ||||
| CAPTION "Rufus 3.11.1668" | ||||
| 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,11,1667,0 | ||||
|  PRODUCTVERSION 3,11,1667,0 | ||||
|  FILEVERSION 3,11,1668,0 | ||||
|  PRODUCTVERSION 3,11,1668,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.11.1667" | ||||
|             VALUE "FileVersion", "3.11.1668" | ||||
|             VALUE "InternalName", "Rufus" | ||||
|             VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" | ||||
|             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" | ||||
|             VALUE "OriginalFilename", "rufus-3.11.exe" | ||||
|             VALUE "ProductName", "Rufus" | ||||
|             VALUE "ProductVersion", "3.11.1667" | ||||
|             VALUE "ProductVersion", "3.11.1668" | ||||
|         END | ||||
|     END | ||||
|     BLOCK "VarFileInfo" | ||||
|  |  | |||
							
								
								
									
										16
									
								
								src/stdio.c
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								src/stdio.c
									
										
									
									
									
								
							|  | @ -674,12 +674,26 @@ char* GuidToString(const GUID* guid) | |||
| 
 | ||||
| 	if (guid == NULL) return NULL; | ||||
| 	sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||||
| 		(unsigned int)guid->Data1, guid->Data2, guid->Data3, | ||||
| 		(uint32_t)guid->Data1, guid->Data2, guid->Data3, | ||||
| 		guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], | ||||
| 		guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); | ||||
| 	return guid_string; | ||||
| } | ||||
| 
 | ||||
| GUID* StringToGuid(const char* str) | ||||
| { | ||||
| 	static GUID guid; | ||||
| 
 | ||||
| 	if (str == NULL) return NULL; | ||||
| 	if (sscanf(str, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||||
| 		(uint32_t*)&guid.Data1, (uint32_t*)&guid.Data2, (uint32_t*)&guid.Data3, | ||||
| 		(uint32_t*)&guid.Data4[0], (uint32_t*)&guid.Data4[1], (uint32_t*)&guid.Data4[2], | ||||
| 		(uint32_t*)&guid.Data4[3], (uint32_t*)&guid.Data4[4], (uint32_t*)&guid.Data4[5], | ||||
| 		(uint32_t*)&guid.Data4[6], (uint32_t*)&guid.Data4[7]) != 11) | ||||
| 		return NULL; | ||||
| 	return &guid; | ||||
| } | ||||
| 
 | ||||
| // Find upper power of 2
 | ||||
| static __inline uint16_t upo2(uint16_t v) | ||||
| { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue