-/******************************************************************************
- * Utility.c
- *
- * Copyright (c) 2020, longpanda <admin@ventoy.net>
- *
- * 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 the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-#include <Windows.h>
-#include "Ventoy2Disk.h"
-
-void Log(const char *Fmt, ...)
-{
- va_list Arg;
- int Len = 0;
- FILE *File = NULL;
- SYSTEMTIME Sys;
- char szBuf[1024];
-
- GetLocalTime(&Sys);
- Len += safe_sprintf(szBuf,
- "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
- Sys.wYear, Sys.wMonth, Sys.wDay,
- Sys.wHour, Sys.wMinute, Sys.wSecond,
- Sys.wMilliseconds);
-
- va_start(Arg, Fmt);
- Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
- va_end(Arg);
-
- //printf("%s\n", szBuf);
-
-#if 1
- fopen_s(&File, VENTOY_FILE_LOG, "a+");
- if (File)
- {
- fwrite(szBuf, 1, Len, File);
- fwrite("\n", 1, 1, File);
- fclose(File);
- }
-#endif
-
-}
-
-BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
-{
- va_list Arg;
- HANDLE hFile;
- DWORD Attr;
- CHAR FilePath[MAX_PATH];
-
- va_start(Arg, Fmt);
- vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
- va_end(Arg);
-
- hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
- if (INVALID_HANDLE_VALUE == hFile)
- {
- return FALSE;
- }
-
- CloseHandle(hFile);
-
- Attr = GetFileAttributesA(FilePath);
-
- if (Dir)
- {
- if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
- {
- return FALSE;
- }
- }
- else
- {
- if (Attr & FILE_ATTRIBUTE_DIRECTORY)
- {
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-
-int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)
-{
- int FileSize;
- FILE *File = NULL;
- void *Data = NULL;
-
- fopen_s(&File, FileName, "rb");
- if (File == NULL)
- {
- Log("Failed to open file %s", FileName);
- return 1;
- }
-
- fseek(File, 0, SEEK_END);
- FileSize = (int)ftell(File);
-
- Data = malloc(FileSize + ExtLen);
- if (!Data)
- {
- fclose(File);
- return 1;
- }
-
- fseek(File, 0, SEEK_SET);
- fread(Data, 1, FileSize, File);
-
- fclose(File);
-
- *Bufer = Data;
- *BufLen = FileSize;
-
- return 0;
-}
-
-const CHAR* GetLocalVentoyVersion(void)
-{
- int rc;
- int FileSize;
- CHAR *Pos = NULL;
- CHAR *Buf = NULL;
- static CHAR LocalVersion[64] = { 0 };
-
- if (LocalVersion[0] == 0)
- {
- rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);
- if (rc)
- {
- return "";
- }
- Buf[FileSize] = 0;
-
- for (Pos = Buf; *Pos; Pos++)
- {
- if (*Pos == '\r' || *Pos == '\n')
- {
- *Pos = 0;
- break;
- }
- }
-
- safe_sprintf(LocalVersion, "%s", Buf);
- free(Buf);
- }
-
- return LocalVersion;
-}
-
-const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
-{
- CHAR *Pos = NULL;
- CHAR *End = NULL;
- static CHAR LocalVersion[64] = { 0 };
-
- Pos = strstr(Buf, "VENTOY_VERSION=");
- if (Pos)
- {
- Pos += strlen("VENTOY_VERSION=");
- if (*Pos == '"')
- {
- Pos++;
- }
-
- End = Pos;
- while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
- {
- End++;
- }
-
- *End = 0;
-
- safe_sprintf(LocalVersion, "%s", Pos);
- return LocalVersion;
- }
-
- return "";
-}
-
-BOOL IsWow64(void)
-{
- typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
- LPFN_ISWOW64PROCESS fnIsWow64Process;
- BOOL bIsWow64 = FALSE;
-
- fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
- if (NULL != fnIsWow64Process)
- {
- fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
- }
-
- return bIsWow64;
-}
-
-void DumpWindowsVersion(void)
-{
- int Bit;
- BOOL WsVer;
- DWORD Major, Minor;
- ULONGLONG MajorEqual, MinorEqual;
- OSVERSIONINFOEXA Ver1, Ver2;
- const CHAR *Ver = NULL;
- CHAR WinVer[256] = { 0 };
-
- memset(&Ver1, 0, sizeof(Ver1));
- memset(&Ver2, 0, sizeof(Ver2));
-
- Ver1.dwOSVersionInfoSize = sizeof(Ver1);
-
- // suppress the C4996 warning for GetVersionExA
- #pragma warning(push)
- #pragma warning(disable:4996)
- if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
- {
- memset(&Ver1, 0, sizeof(Ver1));
- Ver1.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
- if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
- {
- return;
- }
- }
- #pragma warning(pop)
-
- if (Ver1.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- if (Ver1.dwMajorVersion > 6 || (Ver1.dwMajorVersion == 6 && Ver1.dwMinorVersion >= 2))
- {
- // GetVersionEx() has problem on some Windows version
-
- MajorEqual = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
- for (Major = Ver1.dwMajorVersion; Major <= 9; Major++)
- {
- memset(&Ver2, 0, sizeof(Ver2));
- Ver2.dwOSVersionInfoSize = sizeof(Ver2);
- Ver2.dwMajorVersion = Major;
-
- if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual))
- {
- continue;
- }
-
- if (Ver1.dwMajorVersion < Major)
- {
- Ver1.dwMajorVersion = Major;
- Ver1.dwMinorVersion = 0;
- }
-
- MinorEqual = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
- for (Minor = Ver1.dwMinorVersion; Minor <= 9; Minor++)
- {
- memset(&Ver2, 0, sizeof(Ver2));
-
- Ver2.dwOSVersionInfoSize = sizeof(Ver2);
- Ver2.dwMinorVersion = Minor;
-
- if (!VerifyVersionInfoA(&Ver2, VER_MINORVERSION, MinorEqual))
- {
- continue;
- }
-
- Ver1.dwMinorVersion = Minor;
- break;
- }
-
- break;
- }
- }
-
- if (Ver1.dwMajorVersion <= 0xF && Ver1.dwMinorVersion <= 0xF)
- {
- WsVer = (Ver1.wProductType <= VER_NT_WORKSTATION);
- switch ((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion)
- {
- case 0x51:
- {
- Ver = "XP";
- break;
- }
- case 0x52:
- {
- Ver = GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003";
- break;
- }
- case 0x60:
- {
- Ver = WsVer ? "Vista" : "Server 2008";
- break;
- }
- case 0x61:
- {
- Ver = WsVer ? "7" : "Server 2008 R2";
- break;
- }
- case 0x62:
- {
- Ver = WsVer ? "8" : "Server 2012";
- break;
- }
- case 0x63:
- {
- Ver = WsVer ? "8.1" : "Server 2012 R2";
- break;
- }
- case 0x64:
- {
- Ver = WsVer ? "10 (Preview 1)" : "Server 10 (Preview 1)";
- break;
- }
- case 0xA0:
- {
- Ver = WsVer ? "10" : ((Ver1.dwBuildNumber > 15000) ? "Server 2019" : "Server 2016");
- break;
- }
- default:
- {
- Ver = "10 or later";
- break;
- }
- }
- }
- }
-
- Bit = IsWow64() ? 64 : 32;
-
- if (Ver1.wServicePackMinor)
- {
- safe_sprintf(WinVer, "Windows %s SP%u.%u %d-bit", Ver, Ver1.wServicePackMajor, Ver1.wServicePackMinor, Bit);
- }
- else if (Ver1.wServicePackMajor)
- {
- safe_sprintf(WinVer, "Windows %s SP%u %d-bit", Ver, Ver1.wServicePackMajor, Bit);
- }
- else
- {
- safe_sprintf(WinVer, "Windows %s %d-bit", Ver, Bit);
- }
-
- if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62)
- {
- Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber);
- }
- else
- {
- Log("Windows Version : %s", WinVer);
- }
-
- return;
-}
-
-BOOL IsVentoyLogicalDrive(CHAR DriveLetter)
-{
- int i;
- CONST CHAR *Files[] =
- {
- "EFI\\BOOT\\BOOTX64.EFI",
- "grub\\themes\\ventoy\\theme.txt",
- "ventoy\\ventoy.cpio",
- };
-
- for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)
- {
- if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))
- {
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-
-static int VentoyFillLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
-{
- BYTE Head;
- BYTE Sector;
- BYTE nSector = 63;
- BYTE nHead = 8;
- UINT32 Cylinder;
- UINT32 EndSectorId;
-
- while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
- {
- nHead = (BYTE)nHead * 2;
- }
-
- if (nHead == 0)
- {
- nHead = 255;
- }
-
- Cylinder = StartSectorId / nSector / nHead;
- Head = StartSectorId / nSector % nHead;
- Sector = StartSectorId % nSector + 1;
-
- Table->StartHead = Head;
- Table->StartSector = Sector;
- Table->StartCylinder = Cylinder;
-
- EndSectorId = StartSectorId + SectorCount - 1;
- Cylinder = EndSectorId / nSector / nHead;
- Head = EndSectorId / nSector % nHead;
- Sector = EndSectorId % nSector + 1;
-
- Table->EndHead = Head;
- Table->EndSector = Sector;
- Table->EndCylinder = Cylinder;
-
- Table->StartSectorId = StartSectorId;
- Table->SectorCount = SectorCount;
-
- return 0;
-}
-
-int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)
-{
- GUID Guid;
- int ReservedValue;
- UINT32 DiskSignature;
- UINT32 DiskSectorCount;
- UINT32 PartSectorCount;
- UINT32 PartStartSector;
- UINT32 ReservedSector;
-
- VentoyGetLocalBootImg(pMBR);
-
- CoCreateGuid(&Guid);
-
- memcpy(&DiskSignature, &Guid, sizeof(UINT32));
-
- Log("Disk signature: 0x%08x", DiskSignature);
-
- *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;
-
- DiskSectorCount = (UINT32)(DiskSizeBytes / 512);
-
- ReservedValue = GetReservedSpaceInMB();
- if (ReservedValue <= 0)
- {
- ReservedSector = 0;
- }
- else
- {
- ReservedSector = (UINT32)(ReservedValue * 2048);
- }
-
- Log("ReservedSector: %u", ReservedSector);
-
- //Part1
- PartStartSector = VENTOY_PART1_START_SECTOR;
- PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;
- VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);
-
- pMBR->PartTbl[0].Active = 0x80; // bootable
- pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
-
- //Part2
- PartStartSector += PartSectorCount;
- PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
- VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
-
- pMBR->PartTbl[1].Active = 0x00;
- pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
-
- pMBR->Byte55 = 0x55;
- pMBR->ByteAA = 0xAA;
-
- return 0;
-}
-
-CHAR GetFirstUnusedDriveLetter(void)
-{
- CHAR Letter = 'D';
- DWORD Drives = GetLogicalDrives();
-
- Drives >>= 3;
- while (Drives & 0x1)
- {
- Letter++;
- Drives >>= 1;
- }
-
- return Letter;
-}
-
-const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)
-{
- switch (Type)
- {
- case BusTypeUnknown: return "unknown";
- case BusTypeScsi: return "SCSI";
- case BusTypeAtapi: return "Atapi";
- case BusTypeAta: return "ATA";
- case BusType1394: return "1394";
- case BusTypeSsa: return "SSA";
- case BusTypeFibre: return "Fibre";
- case BusTypeUsb: return "USB";
- case BusTypeRAID: return "RAID";
- case BusTypeiScsi: return "iSCSI";
- case BusTypeSas: return "SAS";
- case BusTypeSata: return "SATA";
- case BusTypeSd: return "SD";
- case BusTypeMmc: return "MMC";
- case BusTypeVirtual: return "Virtual";
- case BusTypeFileBackedVirtual: return "FileBackedVirtual";
- case BusTypeSpaces: return "Spaces";
- case BusTypeNvme: return "Nvme";
- }
- return "unknown";
-}
-
-int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
-{
- int Len = 0;
- BYTE *ImgBuf = NULL;
- static int Loaded = 0;
- static MBR_HEAD MBR;
-
- if (Loaded)
- {
- memcpy(pMBR, &MBR, 512);
- return 0;
- }
-
- if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))
- {
- Log("Copy boot img success");
- memcpy(pMBR, ImgBuf, 512);
- free(ImgBuf);
-
- CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));
-
- memcpy(&MBR, pMBR, 512);
- Loaded = 1;
-
- return 0;
- }
- else
- {
- Log("Copy boot img failed");
- return 1;
- }
-}
-
-int GetHumanReadableGBSize(UINT64 SizeBytes)
-{
- int i;
- int Pow2 = 1;
- double Delta;
- double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
-
- for (i = 0; i < 12; i++)
- {
- if (Pow2 > GB)
- {
- Delta = (Pow2 - GB) / Pow2;
- }
- else
- {
- Delta = (GB - Pow2) / Pow2;
- }
-
- if (Delta < 0.05)
- {
- return Pow2;
- }
-
- Pow2 <<= 1;
- }
-
- return (int)GB;
-}
-
-void TrimString(CHAR *String)
-{
- CHAR *Pos1 = String;
- CHAR *Pos2 = String;
- size_t Len = strlen(String);
-
- while (Len > 0)
- {
- if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
- {
- break;
- }
- String[Len - 1] = 0;
- Len--;
- }
-
- while (*Pos1 == ' ' || *Pos1 == '\t')
- {
- Pos1++;
- }
-
- while (*Pos1)
- {
- *Pos2++ = *Pos1++;
- }
- *Pos2++ = 0;
-
- return;
-}
-
-int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)
-{
- HKEY hKey;
- DWORD Type;
- DWORD Size;
- LSTATUS lRet;
- DWORD Value;
-
- lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);
- Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);
-
- if (ERROR_SUCCESS == lRet)
- {
- Size = sizeof(Value);
- lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);
- Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value);
-
- *pValue = Value;
- RegCloseKey(hKey);
-
- return 0;
- }
- else
- {
- return 1;
- }
-}
-
-int GetPhysicalDriveCount(void)
-{
- DWORD Value;
- int Count = 0;
-
- if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)
- {
- Count = (int)Value;
- }
-
- Log("GetPhysicalDriveCount: %d", Count);
- return Count;
-}
-
-
-
+/******************************************************************************\r
+ * Utility.c\r
+ *\r
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>\r
+ * Copyright (c) 2011-2020, Pete Batard <pete@akeo.ie>\r
+ *\r
+ * This program is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU General Public License as\r
+ * published by the Free Software Foundation; either version 3 of the\r
+ * License, or (at your option) any later version.\r
+ * \r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+#include <Windows.h>\r
+#include "Ventoy2Disk.h"\r
+\r
+void TraceOut(const char *Fmt, ...)\r
+{\r
+ va_list Arg;\r
+ int Len = 0;\r
+ FILE *File = NULL;\r
+ char szBuf[1024];\r
+\r
+ va_start(Arg, Fmt);\r
+ Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);\r
+ va_end(Arg);\r
+\r
+ fopen_s(&File, VENTOY_FILE_LOG, "a+");\r
+ if (File)\r
+ {\r
+ fwrite(szBuf, 1, Len, File);\r
+ fclose(File);\r
+ }\r
+}\r
+\r
+typedef struct LogBuf\r
+{\r
+ int Len; \r
+ char szBuf[1024]; \r
+ struct LogBuf* next;\r
+}LogBuf;\r
+\r
+static BOOL g_LogCache = FALSE;\r
+static LogBuf* g_LogHead = NULL;\r
+static LogBuf* g_LogTail = NULL;\r
+\r
+void LogCache(BOOL cache)\r
+{\r
+ g_LogCache = cache;\r
+}\r
+\r
+void LogFlush(void)\r
+{\r
+ FILE* File = NULL;\r
+ LogBuf* Node = NULL;\r
+ LogBuf* Next = NULL;\r
+\r
+ if (g_CLI_Mode)\r
+ {\r
+ fopen_s(&File, VENTOY_CLI_LOG, "a+");\r
+ }\r
+ else\r
+ {\r
+ fopen_s(&File, VENTOY_FILE_LOG, "a+");\r
+ }\r
+\r
+ if (File)\r
+ {\r
+ for (Node = g_LogHead; Node; Node = Node->next)\r
+ {\r
+ fwrite(Node->szBuf, 1, Node->Len, File);\r
+ fwrite("\n", 1, 1, File);\r
+ }\r
+ fclose(File);\r
+ }\r
+\r
+ for (Node = g_LogHead; Node; Node = Next)\r
+ {\r
+ Next = Node->next;\r
+ free(Node);\r
+ }\r
+\r
+ g_LogHead = g_LogTail = NULL;\r
+}\r
+\r
+void Log(const char *Fmt, ...)\r
+{\r
+ va_list Arg;\r
+ int Len = 0;\r
+ FILE *File = NULL;\r
+ SYSTEMTIME Sys;\r
+ char szBuf[1024];\r
+\r
+ GetLocalTime(&Sys);\r
+ Len += safe_sprintf(szBuf,\r
+ "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",\r
+ Sys.wYear, Sys.wMonth, Sys.wDay,\r
+ Sys.wHour, Sys.wMinute, Sys.wSecond,\r
+ Sys.wMilliseconds);\r
+\r
+ va_start(Arg, Fmt);\r
+ Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len - 1, sizeof(szBuf)-Len-1, Fmt, Arg);\r
+ va_end(Arg);\r
+\r
+ if (g_LogCache)\r
+ {\r
+ LogBuf* Node = NULL;\r
+ Node = malloc(sizeof(LogBuf));\r
+ if (Node)\r
+ {\r
+ memcpy(Node->szBuf, szBuf, Len);\r
+ Node->next = NULL;\r
+ Node->Len = Len;\r
+\r
+ if (g_LogTail)\r
+ {\r
+ g_LogTail->next = Node;\r
+ g_LogTail = Node;\r
+ }\r
+ else\r
+ {\r
+ g_LogHead = g_LogTail = Node;\r
+ }\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+ if (g_CLI_Mode)\r
+ {\r
+ fopen_s(&File, VENTOY_CLI_LOG, "a+");\r
+ }\r
+ else\r
+ {\r
+ fopen_s(&File, VENTOY_FILE_LOG, "a+");\r
+ }\r
+ if (File)\r
+ {\r
+ fwrite(szBuf, 1, Len, File);\r
+ fwrite("\n", 1, 1, File);\r
+ fclose(File);\r
+ }\r
+}\r
+\r
+const char* GUID2String(void *guid, char *buf, int len)\r
+{\r
+ GUID* pGUID = (GUID*)guid;\r
+ sprintf_s(buf, len, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",\r
+ pGUID->Data1, pGUID->Data2, pGUID->Data3,\r
+ pGUID->Data4[0], pGUID->Data4[1],\r
+ pGUID->Data4[2], pGUID->Data4[3], pGUID->Data4[4], pGUID->Data4[5], pGUID->Data4[6], pGUID->Data4[7]\r
+ );\r
+ return buf;\r
+}\r
+\r
+BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)\r
+{\r
+ va_list Arg;\r
+ HANDLE hFile;\r
+ DWORD Attr;\r
+ CHAR FilePath[MAX_PATH];\r
+\r
+ va_start(Arg, Fmt);\r
+ vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);\r
+ va_end(Arg);\r
+\r
+ hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);\r
+ if (INVALID_HANDLE_VALUE == hFile)\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ CloseHandle(hFile);\r
+\r
+ Attr = GetFileAttributesA(FilePath);\r
+\r
+ if (Dir)\r
+ {\r
+ if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (Attr & FILE_ATTRIBUTE_DIRECTORY)\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+int SaveBufToFile(const CHAR *FileName, const void *Buffer, int BufLen)\r
+{\r
+ FILE *File = NULL;\r
+ void *Data = NULL;\r
+\r
+ fopen_s(&File, FileName, "wb");\r
+ if (File == NULL)\r
+ {\r
+ Log("Failed to open file %s", FileName);\r
+ return 1;\r
+ }\r
+\r
+ fwrite(Buffer, 1, BufLen, File);\r
+ fclose(File);\r
+ return 0;\r
+}\r
+\r
+int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)\r
+{\r
+ int FileSize;\r
+ FILE *File = NULL;\r
+ void *Data = NULL;\r
+\r
+ fopen_s(&File, FileName, "rb");\r
+ if (File == NULL)\r
+ {\r
+ Log("Failed to open file %s", FileName);\r
+ return 1;\r
+ }\r
+\r
+ fseek(File, 0, SEEK_END);\r
+ FileSize = (int)ftell(File);\r
+\r
+ Data = malloc(FileSize + ExtLen);\r
+ if (!Data)\r
+ {\r
+ fclose(File);\r
+ return 1;\r
+ }\r
+\r
+ fseek(File, 0, SEEK_SET);\r
+ fread(Data, 1, FileSize, File);\r
+\r
+ fclose(File);\r
+\r
+ *Bufer = Data;\r
+ *BufLen = FileSize;\r
+\r
+ return 0;\r
+}\r
+\r
+const CHAR* GetLocalVentoyVersion(void)\r
+{\r
+ int rc;\r
+ int FileSize;\r
+ CHAR *Pos = NULL;\r
+ CHAR *Buf = NULL;\r
+ static CHAR LocalVersion[64] = { 0 };\r
+\r
+ if (LocalVersion[0] == 0)\r
+ {\r
+ rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);\r
+ if (rc)\r
+ {\r
+ return "";\r
+ }\r
+ Buf[FileSize] = 0;\r
+\r
+ for (Pos = Buf; *Pos; Pos++)\r
+ {\r
+ if (*Pos == '\r' || *Pos == '\n')\r
+ {\r
+ *Pos = 0;\r
+ break;\r
+ }\r
+ }\r
+\r
+ safe_sprintf(LocalVersion, "%s", Buf);\r
+ free(Buf);\r
+ }\r
+ \r
+ return LocalVersion;\r
+}\r
+\r
+const CHAR* ParseVentoyVersionFromString(CHAR *Buf)\r
+{\r
+ CHAR *Pos = NULL;\r
+ CHAR *End = NULL;\r
+ static CHAR LocalVersion[64] = { 0 };\r
+\r
+ Pos = strstr(Buf, "VENTOY_VERSION=");\r
+ if (Pos)\r
+ {\r
+ Pos += strlen("VENTOY_VERSION=");\r
+ if (*Pos == '"')\r
+ {\r
+ Pos++;\r
+ }\r
+\r
+ End = Pos;\r
+ while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')\r
+ {\r
+ End++;\r
+ }\r
+\r
+ *End = 0;\r
+\r
+ safe_sprintf(LocalVersion, "%s", Pos);\r
+ return LocalVersion;\r
+ }\r
+\r
+ return "";\r
+}\r
+\r
+BOOL IsWow64(void)\r
+{\r
+ typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);\r
+ LPFN_ISWOW64PROCESS fnIsWow64Process;\r
+ BOOL bIsWow64 = FALSE;\r
+ CHAR Wow64Dir[MAX_PATH];\r
+\r
+ fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");\r
+ if (NULL != fnIsWow64Process)\r
+ {\r
+ fnIsWow64Process(GetCurrentProcess(), &bIsWow64);\r
+ }\r
+\r
+ if (!bIsWow64)\r
+ {\r
+ if (GetSystemWow64DirectoryA(Wow64Dir, sizeof(Wow64Dir)))\r
+ {\r
+ Log("GetSystemWow64DirectoryA=<%s>", Wow64Dir);\r
+ bIsWow64 = TRUE;\r
+ }\r
+ }\r
+\r
+ return bIsWow64;\r
+}\r
+\r
+/*\r
+* Some code and functions in the file are copied from rufus.\r
+* https://github.com/pbatard/rufus\r
+*/\r
+\r
+/* Windows versions */\r
+enum WindowsVersion {\r
+ WINDOWS_UNDEFINED = -1,\r
+ WINDOWS_UNSUPPORTED = 0,\r
+ WINDOWS_XP = 0x51,\r
+ WINDOWS_2003 = 0x52, // Also XP_64\r
+ WINDOWS_VISTA = 0x60, // Also Server 2008\r
+ WINDOWS_7 = 0x61, // Also Server 2008_R2\r
+ WINDOWS_8 = 0x62, // Also Server 2012\r
+ WINDOWS_8_1 = 0x63, // Also Server 2012_R2\r
+ WINDOWS_10_PREVIEW1 = 0x64,\r
+ WINDOWS_10 = 0xA0, // Also Server 2016, also Server 2019\r
+ WINDOWS_11 = 0xB0, // Also Server 2022\r
+ WINDOWS_MAX\r
+};\r
+\r
+static const char* GetEdition(DWORD ProductType)\r
+{\r
+ // From: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo\r
+ // These values can be found in the winnt.h header.\r
+ switch (ProductType) {\r
+ case 0x00000000: return ""; // Undefined\r
+ case 0x00000001: return "Ultimate";\r
+ case 0x00000002: return "Home Basic";\r
+ case 0x00000003: return "Home Premium";\r
+ case 0x00000004: return "Enterprise";\r
+ case 0x00000005: return "Home Basic N";\r
+ case 0x00000006: return "Business";\r
+ case 0x00000007: return "Server Standard";\r
+ case 0x00000008: return "Server Datacenter";\r
+ case 0x00000009: return "Smallbusiness Server";\r
+ case 0x0000000A: return "Server Enterprise";\r
+ case 0x0000000B: return "Starter";\r
+ case 0x0000000C: return "Server Datacenter (Core)";\r
+ case 0x0000000D: return "Server Standard (Core)";\r
+ case 0x0000000E: return "Server Enterprise (Core)";\r
+ case 0x00000010: return "Business N";\r
+ case 0x00000011: return "Web Server";\r
+ case 0x00000012: return "HPC Edition";\r
+ case 0x00000013: return "Storage Server (Essentials)";\r
+ case 0x0000001A: return "Home Premium N";\r
+ case 0x0000001B: return "Enterprise N";\r
+ case 0x0000001C: return "Ultimate N";\r
+ case 0x00000022: return "Home Server";\r
+ case 0x00000024: return "Server Standard without Hyper-V";\r
+ case 0x00000025: return "Server Datacenter without Hyper-V";\r
+ case 0x00000026: return "Server Enterprise without Hyper-V";\r
+ case 0x00000027: return "Server Datacenter without Hyper-V (Core)";\r
+ case 0x00000028: return "Server Standard without Hyper-V (Core)";\r
+ case 0x00000029: return "Server Enterprise without Hyper-V (Core)";\r
+ case 0x0000002A: return "Hyper-V Server";\r
+ case 0x0000002F: return "Starter N";\r
+ case 0x00000030: return "Pro";\r
+ case 0x00000031: return "Pro N";\r
+ case 0x00000034: return "Server Solutions Premium";\r
+ case 0x00000035: return "Server Solutions Premium (Core)";\r
+ case 0x00000040: return "Server Hyper Core V";\r
+ case 0x00000042: return "Starter E";\r
+ case 0x00000043: return "Home Basic E";\r
+ case 0x00000044: return "Premium E";\r
+ case 0x00000045: return "Pro E";\r
+ case 0x00000046: return "Enterprise E";\r
+ case 0x00000047: return "Ultimate E";\r
+ case 0x00000048: return "Enterprise (Eval)";\r
+ case 0x0000004F: return "Server Standard (Eval)";\r
+ case 0x00000050: return "Server Datacenter (Eval)";\r
+ case 0x00000054: return "Enterprise N (Eval)";\r
+ case 0x00000057: return "Thin PC";\r
+ case 0x00000058: case 0x00000059: case 0x0000005A: case 0x0000005B: case 0x0000005C: return "Embedded";\r
+ case 0x00000062: return "Home N";\r
+ case 0x00000063: return "Home China";\r
+ case 0x00000064: return "Home Single Language";\r
+ case 0x00000065: return "Home";\r
+ case 0x00000067: return "Pro with Media Center";\r
+ case 0x00000069: case 0x0000006A: case 0x0000006B: case 0x0000006C: return "Embedded";\r
+ case 0x0000006F: return "Home Connected";\r
+ case 0x00000070: return "Pro Student";\r
+ case 0x00000071: return "Home Connected N";\r
+ case 0x00000072: return "Pro Student N";\r
+ case 0x00000073: return "Home Connected Single Language";\r
+ case 0x00000074: return "Home Connected China";\r
+ case 0x00000079: return "Education";\r
+ case 0x0000007A: return "Education N";\r
+ case 0x0000007D: return "Enterprise LTSB";\r
+ case 0x0000007E: return "Enterprise LTSB N";\r
+ case 0x0000007F: return "Pro S";\r
+ case 0x00000080: return "Pro S N";\r
+ case 0x00000081: return "Enterprise LTSB (Eval)";\r
+ case 0x00000082: return "Enterprise LTSB N (Eval)";\r
+ case 0x0000008A: return "Pro Single Language";\r
+ case 0x0000008B: return "Pro China";\r
+ case 0x0000008C: return "Enterprise Subscription";\r
+ case 0x0000008D: return "Enterprise Subscription N";\r
+ case 0x00000091: return "Server Datacenter SA (Core)";\r
+ case 0x00000092: return "Server Standard SA (Core)";\r
+ case 0x00000095: return "Utility VM";\r
+ case 0x000000A1: return "Pro for Workstations";\r
+ case 0x000000A2: return "Pro for Workstations N";\r
+ case 0x000000A4: return "Pro for Education";\r
+ case 0x000000A5: return "Pro for Education N";\r
+ case 0x000000AB: return "Enterprise G"; // I swear Microsoft are just making up editions...\r
+ case 0x000000AC: return "Enterprise G N";\r
+ case 0x000000B6: return "Home OS";\r
+ case 0x000000B7: return "Cloud E";\r
+ case 0x000000B8: return "Cloud E N";\r
+ case 0x000000BD: return "Lite";\r
+ case 0xABCDABCD: return "(Unlicensed)";\r
+ default: return "(Unknown Edition)";\r
+ }\r
+}\r
+\r
+#define is_x64 IsWow64\r
+#define static_strcpy safe_strcpy \r
+#define REGKEY_HKCU HKEY_CURRENT_USER\r
+#define REGKEY_HKLM HKEY_LOCAL_MACHINE\r
+static int nWindowsVersion = WINDOWS_UNDEFINED;\r
+static int nWindowsBuildNumber = -1;\r
+static char WindowsVersionStr[128] = "";\r
+\r
+/* Helpers for 32 bit registry operations */\r
+\r
+/*\r
+* Read a generic registry key value. If a short key_name is used, assume that\r
+* it belongs to the application and create the app subkey if required\r
+*/\r
+static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type,\r
+ LPBYTE dest, DWORD dest_size)\r
+{\r
+ const char software_prefix[] = "SOFTWARE\\";\r
+ char long_key_name[MAX_PATH] = { 0 };\r
+ BOOL r = FALSE;\r
+ size_t i;\r
+ LONG s;\r
+ HKEY hSoftware = NULL, hApp = NULL;\r
+ DWORD dwType = -1, dwSize = dest_size;\r
+\r
+ memset(dest, 0, dest_size);\r
+\r
+ if (key_name == NULL)\r
+ return FALSE;\r
+\r
+ for (i = strlen(key_name); i>0; i--) {\r
+ if (key_name[i] == '\\')\r
+ break;\r
+ }\r
+\r
+ if (i > 0) {\r
+ // Prefix with "SOFTWARE" if needed\r
+ if (_strnicmp(key_name, software_prefix, sizeof(software_prefix)-1) != 0) {\r
+ if (i + sizeof(software_prefix) >= sizeof(long_key_name))\r
+ return FALSE;\r
+ strcpy_s(long_key_name, sizeof(long_key_name), software_prefix);\r
+ strcat_s(long_key_name, sizeof(long_key_name), key_name);\r
+ long_key_name[sizeof(software_prefix)+i - 1] = 0;\r
+ }\r
+ else {\r
+ if (i >= sizeof(long_key_name))\r
+ return FALSE;\r
+ static_strcpy(long_key_name, key_name);\r
+ long_key_name[i] = 0;\r
+ }\r
+ i++;\r
+ if (RegOpenKeyExA(key_root, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) {\r
+ hApp = NULL;\r
+ goto out;\r
+ }\r
+ }\r
+ else {\r
+ if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ | KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) {\r
+ hSoftware = NULL;\r
+ goto out;\r
+ } \r
+ }\r
+\r
+ s = RegQueryValueExA(hApp, &key_name[i], NULL, &dwType, (LPBYTE)dest, &dwSize);\r
+ // No key means default value of 0 or empty string\r
+ if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType == reg_type) && (dwSize > 0))) {\r
+ r = TRUE;\r
+ }\r
+out:\r
+ if (hSoftware != NULL)\r
+ RegCloseKey(hSoftware);\r
+ if (hApp != NULL)\r
+ RegCloseKey(hApp);\r
+ return r;\r
+}\r
+\r
+#define GetRegistryKey32(root, key, pval) _GetRegistryKey(root, key, REG_DWORD, (LPBYTE)pval, sizeof(DWORD))\r
+static __inline INT32 ReadRegistryKey32(HKEY root, const char* key) {\r
+ DWORD val;\r
+ GetRegistryKey32(root, key, &val);\r
+ return (INT32)val;\r
+}\r
+\r
+/*\r
+* Modified from smartmontools' os_win32.cpp\r
+*/\r
+void GetWindowsVersion(void)\r
+{ \r
+ OSVERSIONINFOEXA vi, vi2;\r
+ DWORD dwProductType;\r
+ const char* w = 0;\r
+ const char* w64 = "32 bit";\r
+ char *vptr;\r
+ size_t vlen;\r
+ unsigned major, minor;\r
+ ULONGLONG major_equal, minor_equal;\r
+ BOOL ws;\r
+\r
+ nWindowsVersion = WINDOWS_UNDEFINED;\r
+ static_strcpy(WindowsVersionStr, "Windows Undefined");\r
+\r
+ // suppress the C4996 warning for GetVersionExA\r
+ #pragma warning(push)\r
+ #pragma warning(disable:4996)\r
+\r
+ memset(&vi, 0, sizeof(vi));\r
+ vi.dwOSVersionInfoSize = sizeof(vi);\r
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {\r
+ memset(&vi, 0, sizeof(vi));\r
+ vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);\r
+ if (!GetVersionExA((OSVERSIONINFOA *)&vi))\r
+ return;\r
+ }\r
+\r
+ #pragma warning(pop)\r
+\r
+ if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {\r
+\r
+ if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {\r
+ // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version\r
+ // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx\r
+ // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS\r
+ // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor...\r
+\r
+ major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);\r
+ for (major = vi.dwMajorVersion; major <= 9; major++) {\r
+ memset(&vi2, 0, sizeof(vi2));\r
+ vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;\r
+ if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))\r
+ continue;\r
+ if (vi.dwMajorVersion < major) {\r
+ vi.dwMajorVersion = major; vi.dwMinorVersion = 0;\r
+ }\r
+\r
+ minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);\r
+ for (minor = vi.dwMinorVersion; minor <= 9; minor++) {\r
+ memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);\r
+ vi2.dwMinorVersion = minor;\r
+ if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))\r
+ continue;\r
+ vi.dwMinorVersion = minor;\r
+ break;\r
+ }\r
+\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {\r
+ ws = (vi.wProductType <= VER_NT_WORKSTATION);\r
+ nWindowsVersion = vi.dwMajorVersion << 4 | vi.dwMinorVersion;\r
+ switch (nWindowsVersion) {\r
+ case WINDOWS_XP: w = "XP";\r
+ break;\r
+ case WINDOWS_2003: w = (ws ? "XP_64" : (!GetSystemMetrics(89) ? "Server 2003" : "Server 2003_R2"));\r
+ break;\r
+ case WINDOWS_VISTA: w = (ws ? "Vista" : "Server 2008");\r
+ break;\r
+ case WINDOWS_7: w = (ws ? "7" : "Server 2008_R2");\r
+ break;\r
+ case WINDOWS_8: w = (ws ? "8" : "Server 2012");\r
+ break;\r
+ case WINDOWS_8_1: w = (ws ? "8.1" : "Server 2012_R2");\r
+ break;\r
+ case WINDOWS_10_PREVIEW1: w = (ws ? "10 (Preview 1)" : "Server 10 (Preview 1)");\r
+ break;\r
+ // Starting with Windows 10 Preview 2, the major is the same as the public-facing version\r
+ case WINDOWS_10:\r
+ if (vi.dwBuildNumber < 20000) {\r
+ w = (ws ? "10" : ((vi.dwBuildNumber < 17763) ? "Server 2016" : "Server 2019"));\r
+ break;\r
+ }\r
+ nWindowsVersion = WINDOWS_11;\r
+ // Fall through\r
+ case WINDOWS_11: w = (ws ? "11" : "Server 2022");\r
+ break;\r
+ default:\r
+ if (nWindowsVersion < WINDOWS_XP)\r
+ nWindowsVersion = WINDOWS_UNSUPPORTED;\r
+ else\r
+ w = "12 or later";\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (is_x64())\r
+ w64 = "64-bit";\r
+\r
+ GetProductInfo(vi.dwMajorVersion, vi.dwMinorVersion, vi.wServicePackMajor, vi.wServicePackMinor, &dwProductType);\r
+ vptr = WindowsVersionStr;\r
+ vlen = sizeof(WindowsVersionStr) - 1;\r
+\r
+ if (!w)\r
+ sprintf_s(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"),\r
+ (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);\r
+ else if (vi.wServicePackMinor)\r
+ sprintf_s(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);\r
+ else if (vi.wServicePackMajor)\r
+ sprintf_s(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64);\r
+ else\r
+ sprintf_s(vptr, vlen, "%s%s%s, %s",\r
+ w, (dwProductType != PRODUCT_UNDEFINED) ? " " : "", GetEdition(dwProductType), w64);\r
+\r
+ // Add the build number (including UBR if available) for Windows 8.0 and later\r
+ nWindowsBuildNumber = vi.dwBuildNumber;\r
+ if (nWindowsVersion >= 0x62) {\r
+ int nUbr = ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows NT\\CurrentVersion\\UBR");\r
+ vptr = WindowsVersionStr + strlen(WindowsVersionStr);\r
+ vlen = sizeof(WindowsVersionStr) - strlen(WindowsVersionStr) - 1;\r
+ if (nUbr > 0)\r
+ sprintf_s(vptr, vlen, " (Build %d.%d)", nWindowsBuildNumber, nUbr);\r
+ else\r
+ sprintf_s(vptr, vlen, " (Build %d)", nWindowsBuildNumber);\r
+ }\r
+}\r
+\r
+\r
+\r
+void DumpWindowsVersion(void)\r
+{\r
+ GetWindowsVersion();\r
+ Log("Windows Version: <<Windows %s>>", WindowsVersionStr);\r
+ return;\r
+}\r
+\r
+\r
+BOOL IsVentoyLogicalDrive(CHAR DriveLetter)\r
+{\r
+ int i;\r
+ CONST CHAR *Files[] =\r
+ {\r
+ "EFI\\BOOT\\BOOTX64.EFI",\r
+ "grub\\themes\\ventoy\\theme.txt",\r
+ "ventoy\\ventoy.cpio",\r
+ };\r
+\r
+ for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)\r
+ {\r
+ if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))\r
+ {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+int VentoyFillMBRLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)\r
+{\r
+ BYTE Head;\r
+ BYTE Sector;\r
+ BYTE nSector = 63;\r
+ BYTE nHead = 8; \r
+ UINT32 Cylinder;\r
+ UINT32 EndSectorId;\r
+\r
+ while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)\r
+ {\r
+ nHead = (BYTE)nHead * 2;\r
+ }\r
+\r
+ if (nHead == 0)\r
+ {\r
+ nHead = 255;\r
+ }\r
+\r
+ Cylinder = StartSectorId / nSector / nHead;\r
+ Head = StartSectorId / nSector % nHead;\r
+ Sector = StartSectorId % nSector + 1;\r
+\r
+ Table->StartHead = Head;\r
+ Table->StartSector = Sector;\r
+ Table->StartCylinder = Cylinder;\r
+\r
+ EndSectorId = StartSectorId + SectorCount - 1;\r
+ Cylinder = EndSectorId / nSector / nHead;\r
+ Head = EndSectorId / nSector % nHead;\r
+ Sector = EndSectorId % nSector + 1;\r
+\r
+ Table->EndHead = Head;\r
+ Table->EndSector = Sector;\r
+ Table->EndCylinder = Cylinder;\r
+\r
+ Table->StartSectorId = StartSectorId;\r
+ Table->SectorCount = SectorCount;\r
+\r
+ return 0;\r
+}\r
+\r
+int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR, int PartStyle, UINT8 FsFlag)\r
+{\r
+ GUID Guid;\r
+ int ReservedValue;\r
+ UINT32 DiskSignature;\r
+ UINT32 DiskSectorCount;\r
+ UINT32 PartSectorCount;\r
+ UINT32 PartStartSector;\r
+ UINT32 ReservedSector;\r
+\r
+ VentoyGetLocalBootImg(pMBR);\r
+\r
+ CoCreateGuid(&Guid);\r
+\r
+ memcpy(&DiskSignature, &Guid, sizeof(UINT32));\r
+\r
+ Log("Disk signature: 0x%08x", DiskSignature);\r
+\r
+ *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;\r
+ memcpy(pMBR->BootCode + 0x180, &Guid, 16);\r
+\r
+ if (DiskSizeBytes / 512 > 0xFFFFFFFF)\r
+ {\r
+ DiskSectorCount = 0xFFFFFFFF;\r
+ }\r
+ else\r
+ {\r
+ DiskSectorCount = (UINT32)(DiskSizeBytes / 512);\r
+ }\r
+\r
+ ReservedValue = GetReservedSpaceInMB();\r
+ if (ReservedValue <= 0)\r
+ {\r
+ ReservedSector = 0;\r
+ }\r
+ else\r
+ {\r
+ ReservedSector = (UINT32)(ReservedValue * 2048);\r
+ }\r
+\r
+ if (PartStyle)\r
+ {\r
+ ReservedSector += 33; // backup GPT part table\r
+ }\r
+\r
+ // check aligned with 4KB\r
+ if (IsPartNeed4KBAlign())\r
+ {\r
+ UINT64 sectors = DiskSizeBytes / 512;\r
+ if (sectors % 8)\r
+ {\r
+ Log("Disk need to align with 4KB %u", (UINT32)(sectors % 8));\r
+ ReservedSector += (UINT32)(sectors % 8);\r
+ }\r
+ }\r
+\r
+ Log("ReservedSector: %u", ReservedSector);\r
+\r
+ //Part1\r
+ PartStartSector = VENTOY_PART1_START_SECTOR;\r
+ PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;\r
+ VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);\r
+\r
+ pMBR->PartTbl[0].Active = 0x80; // bootable\r
+ pMBR->PartTbl[0].FsFlag = FsFlag; // File system flag 07:exFAT/NTFS/HPFS 0C:FAT32\r
+\r
+ //Part2\r
+ PartStartSector += PartSectorCount;\r
+ PartSectorCount = VENTOY_EFI_PART_SIZE / 512;\r
+ VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);\r
+\r
+ pMBR->PartTbl[1].Active = 0x00; \r
+ pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition\r
+\r
+ pMBR->Byte55 = 0x55;\r
+ pMBR->ByteAA = 0xAA;\r
+\r
+ return 0;\r
+}\r
+\r
+\r
+static int VentoyFillProtectMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)\r
+{\r
+ GUID Guid;\r
+ UINT32 DiskSignature;\r
+ UINT64 DiskSectorCount;\r
+\r
+ VentoyGetLocalBootImg(pMBR);\r
+\r
+ CoCreateGuid(&Guid);\r
+\r
+ memcpy(&DiskSignature, &Guid, sizeof(UINT32));\r
+\r
+ Log("Disk signature: 0x%08x", DiskSignature);\r
+\r
+ *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;\r
+ memcpy(pMBR->BootCode + 0x180, &Guid, 16);\r
+\r
+ DiskSectorCount = DiskSizeBytes / 512 - 1;\r
+ if (DiskSectorCount > 0xFFFFFFFF)\r
+ {\r
+ DiskSectorCount = 0xFFFFFFFF;\r
+ }\r
+\r
+ memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));\r
+\r
+ pMBR->PartTbl[0].Active = 0x00;\r
+ pMBR->PartTbl[0].FsFlag = 0xee; // EE\r
+\r
+ pMBR->PartTbl[0].StartHead = 0;\r
+ pMBR->PartTbl[0].StartSector = 1;\r
+ pMBR->PartTbl[0].StartCylinder = 0;\r
+ pMBR->PartTbl[0].EndHead = 254;\r
+ pMBR->PartTbl[0].EndSector = 63;\r
+ pMBR->PartTbl[0].EndCylinder = 1023;\r
+\r
+ pMBR->PartTbl[0].StartSectorId = 1;\r
+ pMBR->PartTbl[0].SectorCount = (UINT32)DiskSectorCount;\r
+\r
+ pMBR->Byte55 = 0x55;\r
+ pMBR->ByteAA = 0xAA;\r
+\r
+ pMBR->BootCode[92] = 0x22;\r
+\r
+ return 0;\r
+}\r
+\r
+int VentoyFillWholeGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)\r
+{\r
+ UINT64 Part1SectorCount = 0;\r
+ UINT64 DiskSectorCount = DiskSizeBytes / 512;\r
+ VTOY_GPT_HDR *Head = &pInfo->Head;\r
+ VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;\r
+ static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };\r
+\r
+ VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);\r
+\r
+ Part1SectorCount = DiskSectorCount - 33 - 2048;\r
+\r
+ memcpy(Head->Signature, "EFI PART", 8);\r
+ Head->Version[2] = 0x01;\r
+ Head->Length = 92;\r
+ Head->Crc = 0;\r
+ Head->EfiStartLBA = 1;\r
+ Head->EfiBackupLBA = DiskSectorCount - 1;\r
+ Head->PartAreaStartLBA = 34;\r
+ Head->PartAreaEndLBA = DiskSectorCount - 34;\r
+ CoCreateGuid(&Head->DiskGuid);\r
+ Head->PartTblStartLBA = 2;\r
+ Head->PartTblTotNum = 128;\r
+ Head->PartTblEntryLen = 128;\r
+\r
+\r
+ memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));\r
+ CoCreateGuid(&(Table[0].PartGuid));\r
+ Table[0].StartLBA = 2048;\r
+ Table[0].LastLBA = 2048 + Part1SectorCount - 1;\r
+ Table[0].Attr = 0;\r
+ memcpy(Table[0].Name, L"Data", 4 * 2);\r
+\r
+ //Update CRC\r
+ Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));\r
+ Head->Crc = VentoyCrc32(Head, Head->Length);\r
+\r
+ return 0;\r
+}\r
+\r
+int VentoyFillGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)\r
+{\r
+ INT64 ReservedValue = 0;\r
+ UINT64 ModSectorCount = 0;\r
+ UINT64 ReservedSector = 33;\r
+ UINT64 Part1SectorCount = 0;\r
+ UINT64 DiskSectorCount = DiskSizeBytes / 512;\r
+ VTOY_GPT_HDR *Head = &pInfo->Head;\r
+ VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;\r
+ static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };\r
+ static GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };\r
+ static GUID BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };\r
+\r
+ VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);\r
+\r
+ ReservedValue = GetReservedSpaceInMB();\r
+ if (ReservedValue > 0)\r
+ {\r
+ ReservedSector += ReservedValue * 2048;\r
+ }\r
+\r
+ Part1SectorCount = DiskSectorCount - ReservedSector - (VENTOY_EFI_PART_SIZE / 512) - 2048;\r
+\r
+ ModSectorCount = (Part1SectorCount % 8);\r
+ if (ModSectorCount)\r
+ {\r
+ Log("Part1SectorCount:%llu is not aligned by 4KB (%llu)", (ULONGLONG)Part1SectorCount, (ULONGLONG)ModSectorCount);\r
+ }\r
+\r
+ // check aligned with 4KB\r
+ if (IsPartNeed4KBAlign())\r
+ {\r
+ if (ModSectorCount)\r
+ {\r
+ Log("Disk need to align with 4KB %u", (UINT32)ModSectorCount);\r
+ Part1SectorCount -= ModSectorCount;\r
+ }\r
+ else\r
+ {\r
+ Log("no need to align with 4KB");\r
+ }\r
+ }\r
+\r
+ memcpy(Head->Signature, "EFI PART", 8);\r
+ Head->Version[2] = 0x01;\r
+ Head->Length = 92;\r
+ Head->Crc = 0;\r
+ Head->EfiStartLBA = 1;\r
+ Head->EfiBackupLBA = DiskSectorCount - 1;\r
+ Head->PartAreaStartLBA = 34;\r
+ Head->PartAreaEndLBA = DiskSectorCount - 34;\r
+ CoCreateGuid(&Head->DiskGuid);\r
+ Head->PartTblStartLBA = 2;\r
+ Head->PartTblTotNum = 128;\r
+ Head->PartTblEntryLen = 128;\r
+\r
+\r
+ memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));\r
+ CoCreateGuid(&(Table[0].PartGuid));\r
+ Table[0].StartLBA = 2048;\r
+ Table[0].LastLBA = 2048 + Part1SectorCount - 1;\r
+ Table[0].Attr = 0;\r
+ memcpy(Table[0].Name, L"Ventoy", 6 * 2);\r
+\r
+ // to fix windows issue\r
+ //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));\r
+ memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(GUID));\r
+ CoCreateGuid(&(Table[1].PartGuid));\r
+ Table[1].StartLBA = Table[0].LastLBA + 1;\r
+ Table[1].LastLBA = Table[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1;\r
+ Table[1].Attr = 0xC000000000000001ULL;\r
+ memcpy(Table[1].Name, L"VTOYEFI", 7 * 2);\r
+\r
+#if 0\r
+ memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(GUID));\r
+ CoCreateGuid(&(Table[2].PartGuid));\r
+ Table[2].StartLBA = 34;\r
+ Table[2].LastLBA = 2047;\r
+ Table[2].Attr = 0;\r
+#endif\r
+\r
+ //Update CRC\r
+ Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));\r
+ Head->Crc = VentoyCrc32(Head, Head->Length);\r
+\r
+ return 0;\r
+}\r
+\r
+int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)\r
+{\r
+ UINT64 LBA;\r
+ UINT64 BackupLBA;\r
+\r
+ memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));\r
+\r
+ LBA = pHead->EfiStartLBA;\r
+ BackupLBA = pHead->EfiBackupLBA;\r
+ \r
+ pHead->EfiStartLBA = BackupLBA;\r
+ pHead->EfiBackupLBA = LBA;\r
+ pHead->PartTblStartLBA = BackupLBA + 1 - 33;\r
+\r
+ pHead->Crc = 0;\r
+ pHead->Crc = VentoyCrc32(pHead, pHead->Length);\r
+\r
+ return 0;\r
+}\r
+\r
+CHAR GetFirstUnusedDriveLetter(void)\r
+{\r
+ CHAR Letter = 'D';\r
+ DWORD Drives = GetLogicalDrives();\r
+\r
+ Drives >>= 3;\r
+ while (Drives & 0x1)\r
+ {\r
+ Letter++;\r
+ Drives >>= 1;\r
+ }\r
+\r
+ return Letter;\r
+}\r
+\r
+const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)\r
+{\r
+ switch (Type)\r
+ {\r
+ case BusTypeUnknown: return "unknown";\r
+ case BusTypeScsi: return "SCSI";\r
+ case BusTypeAtapi: return "Atapi";\r
+ case BusTypeAta: return "ATA";\r
+ case BusType1394: return "1394";\r
+ case BusTypeSsa: return "SSA";\r
+ case BusTypeFibre: return "Fibre";\r
+ case BusTypeUsb: return "USB";\r
+ case BusTypeRAID: return "RAID";\r
+ case BusTypeiScsi: return "iSCSI";\r
+ case BusTypeSas: return "SAS";\r
+ case BusTypeSata: return "SATA";\r
+ case BusTypeSd: return "SD";\r
+ case BusTypeMmc: return "MMC";\r
+ case BusTypeVirtual: return "Virtual";\r
+ case BusTypeFileBackedVirtual: return "FileBackedVirtual";\r
+ case BusTypeSpaces: return "Spaces";\r
+ case BusTypeNvme: return "Nvme";\r
+ }\r
+ return "unknown";\r
+}\r
+\r
+int VentoyGetLocalBootImg(MBR_HEAD *pMBR)\r
+{\r
+ int Len = 0;\r
+ BYTE *ImgBuf = NULL;\r
+ static int Loaded = 0;\r
+ static MBR_HEAD MBR;\r
+\r
+ if (Loaded)\r
+ {\r
+ memcpy(pMBR, &MBR, 512);\r
+ return 0;\r
+ }\r
+\r
+ if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))\r
+ {\r
+ Log("Copy boot img success");\r
+ memcpy(pMBR, ImgBuf, 512);\r
+ free(ImgBuf);\r
+ \r
+ CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));\r
+\r
+ memcpy(&MBR, pMBR, 512);\r
+ Loaded = 1;\r
+\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+ Log("Copy boot img failed");\r
+ return 1;\r
+ }\r
+}\r
+\r
+int GetHumanReadableGBSize(UINT64 SizeBytes)\r
+{\r
+ int i;\r
+ int Pow2 = 1;\r
+ double Delta;\r
+ double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;\r
+\r
+ if ((SizeBytes % 1073741824) == 0)\r
+ {\r
+ return (int)(SizeBytes / 1073741824);\r
+ }\r
+\r
+ for (i = 0; i < 12; i++)\r
+ {\r
+ if (Pow2 > GB)\r
+ {\r
+ Delta = (Pow2 - GB) / Pow2;\r
+ }\r
+ else\r
+ {\r
+ Delta = (GB - Pow2) / Pow2;\r
+ }\r
+\r
+ if (Delta < 0.05)\r
+ {\r
+ return Pow2;\r
+ }\r
+\r
+ Pow2 <<= 1;\r
+ }\r
+\r
+ return (int)GB;\r
+}\r
+\r
+void TrimString(CHAR *String)\r
+{\r
+ CHAR *Pos1 = String;\r
+ CHAR *Pos2 = String;\r
+ size_t Len = strlen(String);\r
+\r
+ while (Len > 0)\r
+ {\r
+ if (String[Len - 1] != ' ' && String[Len - 1] != '\t')\r
+ {\r
+ break;\r
+ }\r
+ String[Len - 1] = 0;\r
+ Len--;\r
+ }\r
+\r
+ while (*Pos1 == ' ' || *Pos1 == '\t')\r
+ {\r
+ Pos1++;\r
+ }\r
+\r
+ while (*Pos1)\r
+ {\r
+ *Pos2++ = *Pos1++;\r
+ }\r
+ *Pos2++ = 0;\r
+\r
+ return;\r
+}\r
+\r
+int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)\r
+{\r
+ HKEY hKey;\r
+ DWORD Type;\r
+ DWORD Size;\r
+ LSTATUS lRet;\r
+ DWORD Value;\r
+\r
+ lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);\r
+ Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);\r
+\r
+ if (ERROR_SUCCESS == lRet)\r
+ {\r
+ Size = sizeof(Value);\r
+ lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);\r
+ Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value);\r
+\r
+ *pValue = Value;\r
+ RegCloseKey(hKey);\r
+\r
+ return 0;\r
+ }\r
+ else\r
+ {\r
+ return 1;\r
+ }\r
+}\r
+\r
+int GetPhysicalDriveCount(void)\r
+{\r
+ DWORD Value;\r
+ int Count = 0;\r
+\r
+ if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)\r
+ {\r
+ Count = (int)Value;\r
+ }\r
+\r
+ Log("GetPhysicalDriveCount: %d", Count);\r
+ return Count;\r
+}\r
+\r
+void VentoyStringToUpper(CHAR* str)\r
+{\r
+ while (str && *str)\r
+ {\r
+ if (*str >= 'a' && *str <= 'z')\r
+ {\r
+ *str = toupper(*str);\r
+ } \r
+ str++;\r
+ }\r
+}\r