]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Ventoy2Disk/Ventoy2Disk/Utility.c
dc9464a8d32bd73a78634c6fda88de60997abc48
[Ventoy.git] / Ventoy2Disk / Ventoy2Disk / Utility.c
1 /******************************************************************************
2 * Utility.c
3 *
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 * Copyright (c) 2011-2020, Pete Batard <pete@akeo.ie>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include <Windows.h>
22 #include "Ventoy2Disk.h"
23
24 void TraceOut(const char *Fmt, ...)
25 {
26 va_list Arg;
27 int Len = 0;
28 FILE *File = NULL;
29 char szBuf[1024];
30
31 va_start(Arg, Fmt);
32 Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
33 va_end(Arg);
34
35 fopen_s(&File, VENTOY_FILE_LOG, "a+");
36 if (File)
37 {
38 fwrite(szBuf, 1, Len, File);
39 fclose(File);
40 }
41 }
42
43 void Log(const char *Fmt, ...)
44 {
45 va_list Arg;
46 int Len = 0;
47 FILE *File = NULL;
48 SYSTEMTIME Sys;
49 char szBuf[1024];
50
51 GetLocalTime(&Sys);
52 Len += safe_sprintf(szBuf,
53 "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
54 Sys.wYear, Sys.wMonth, Sys.wDay,
55 Sys.wHour, Sys.wMinute, Sys.wSecond,
56 Sys.wMilliseconds);
57
58 va_start(Arg, Fmt);
59 Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len - 1, sizeof(szBuf)-Len-1, Fmt, Arg);
60 va_end(Arg);
61
62 if (g_CLI_Mode)
63 {
64 fopen_s(&File, VENTOY_CLI_LOG, "a+");
65 }
66 else
67 {
68 fopen_s(&File, VENTOY_FILE_LOG, "a+");
69 }
70 if (File)
71 {
72 fwrite(szBuf, 1, Len, File);
73 fwrite("\n", 1, 1, File);
74 fclose(File);
75 }
76 }
77
78 const char* GUID2String(void *guid, char *buf, int len)
79 {
80 GUID* pGUID = (GUID*)guid;
81 sprintf_s(buf, len, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
82 pGUID->Data1, pGUID->Data2, pGUID->Data3,
83 pGUID->Data4[0], pGUID->Data4[1],
84 pGUID->Data4[2], pGUID->Data4[3], pGUID->Data4[4], pGUID->Data4[5], pGUID->Data4[6], pGUID->Data4[7]
85 );
86 return buf;
87 }
88
89 BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
90 {
91 va_list Arg;
92 HANDLE hFile;
93 DWORD Attr;
94 CHAR FilePath[MAX_PATH];
95
96 va_start(Arg, Fmt);
97 vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
98 va_end(Arg);
99
100 hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
101 if (INVALID_HANDLE_VALUE == hFile)
102 {
103 return FALSE;
104 }
105
106 CloseHandle(hFile);
107
108 Attr = GetFileAttributesA(FilePath);
109
110 if (Dir)
111 {
112 if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
113 {
114 return FALSE;
115 }
116 }
117 else
118 {
119 if (Attr & FILE_ATTRIBUTE_DIRECTORY)
120 {
121 return FALSE;
122 }
123 }
124
125 return TRUE;
126 }
127
128 int SaveBufToFile(const CHAR *FileName, const void *Buffer, int BufLen)
129 {
130 FILE *File = NULL;
131 void *Data = NULL;
132
133 fopen_s(&File, FileName, "wb");
134 if (File == NULL)
135 {
136 Log("Failed to open file %s", FileName);
137 return 1;
138 }
139
140 fwrite(Buffer, 1, BufLen, File);
141 fclose(File);
142 return 0;
143 }
144
145 int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)
146 {
147 int FileSize;
148 FILE *File = NULL;
149 void *Data = NULL;
150
151 fopen_s(&File, FileName, "rb");
152 if (File == NULL)
153 {
154 Log("Failed to open file %s", FileName);
155 return 1;
156 }
157
158 fseek(File, 0, SEEK_END);
159 FileSize = (int)ftell(File);
160
161 Data = malloc(FileSize + ExtLen);
162 if (!Data)
163 {
164 fclose(File);
165 return 1;
166 }
167
168 fseek(File, 0, SEEK_SET);
169 fread(Data, 1, FileSize, File);
170
171 fclose(File);
172
173 *Bufer = Data;
174 *BufLen = FileSize;
175
176 return 0;
177 }
178
179 const CHAR* GetLocalVentoyVersion(void)
180 {
181 int rc;
182 int FileSize;
183 CHAR *Pos = NULL;
184 CHAR *Buf = NULL;
185 static CHAR LocalVersion[64] = { 0 };
186
187 if (LocalVersion[0] == 0)
188 {
189 rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);
190 if (rc)
191 {
192 return "";
193 }
194 Buf[FileSize] = 0;
195
196 for (Pos = Buf; *Pos; Pos++)
197 {
198 if (*Pos == '\r' || *Pos == '\n')
199 {
200 *Pos = 0;
201 break;
202 }
203 }
204
205 safe_sprintf(LocalVersion, "%s", Buf);
206 free(Buf);
207 }
208
209 return LocalVersion;
210 }
211
212 const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
213 {
214 CHAR *Pos = NULL;
215 CHAR *End = NULL;
216 static CHAR LocalVersion[64] = { 0 };
217
218 Pos = strstr(Buf, "VENTOY_VERSION=");
219 if (Pos)
220 {
221 Pos += strlen("VENTOY_VERSION=");
222 if (*Pos == '"')
223 {
224 Pos++;
225 }
226
227 End = Pos;
228 while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
229 {
230 End++;
231 }
232
233 *End = 0;
234
235 safe_sprintf(LocalVersion, "%s", Pos);
236 return LocalVersion;
237 }
238
239 return "";
240 }
241
242 BOOL IsWow64(void)
243 {
244 typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
245 LPFN_ISWOW64PROCESS fnIsWow64Process;
246 BOOL bIsWow64 = FALSE;
247 CHAR Wow64Dir[MAX_PATH];
248
249 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
250 if (NULL != fnIsWow64Process)
251 {
252 fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
253 }
254
255 if (!bIsWow64)
256 {
257 if (GetSystemWow64DirectoryA(Wow64Dir, sizeof(Wow64Dir)))
258 {
259 Log("GetSystemWow64DirectoryA=<%s>", Wow64Dir);
260 bIsWow64 = TRUE;
261 }
262 }
263
264 return bIsWow64;
265 }
266
267 /*
268 * Some code and functions in the file are copied from rufus.
269 * https://github.com/pbatard/rufus
270 */
271
272 /* Windows versions */
273 enum WindowsVersion {
274 WINDOWS_UNDEFINED = -1,
275 WINDOWS_UNSUPPORTED = 0,
276 WINDOWS_XP = 0x51,
277 WINDOWS_2003 = 0x52, // Also XP_64
278 WINDOWS_VISTA = 0x60, // Also Server 2008
279 WINDOWS_7 = 0x61, // Also Server 2008_R2
280 WINDOWS_8 = 0x62, // Also Server 2012
281 WINDOWS_8_1 = 0x63, // Also Server 2012_R2
282 WINDOWS_10_PREVIEW1 = 0x64,
283 WINDOWS_10 = 0xA0, // Also Server 2016, also Server 2019
284 WINDOWS_11 = 0xB0, // Also Server 2022
285 WINDOWS_MAX
286 };
287
288 static const char* GetEdition(DWORD ProductType)
289 {
290 // From: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
291 // These values can be found in the winnt.h header.
292 switch (ProductType) {
293 case 0x00000000: return ""; // Undefined
294 case 0x00000001: return "Ultimate";
295 case 0x00000002: return "Home Basic";
296 case 0x00000003: return "Home Premium";
297 case 0x00000004: return "Enterprise";
298 case 0x00000005: return "Home Basic N";
299 case 0x00000006: return "Business";
300 case 0x00000007: return "Server Standard";
301 case 0x00000008: return "Server Datacenter";
302 case 0x00000009: return "Smallbusiness Server";
303 case 0x0000000A: return "Server Enterprise";
304 case 0x0000000B: return "Starter";
305 case 0x0000000C: return "Server Datacenter (Core)";
306 case 0x0000000D: return "Server Standard (Core)";
307 case 0x0000000E: return "Server Enterprise (Core)";
308 case 0x00000010: return "Business N";
309 case 0x00000011: return "Web Server";
310 case 0x00000012: return "HPC Edition";
311 case 0x00000013: return "Storage Server (Essentials)";
312 case 0x0000001A: return "Home Premium N";
313 case 0x0000001B: return "Enterprise N";
314 case 0x0000001C: return "Ultimate N";
315 case 0x00000022: return "Home Server";
316 case 0x00000024: return "Server Standard without Hyper-V";
317 case 0x00000025: return "Server Datacenter without Hyper-V";
318 case 0x00000026: return "Server Enterprise without Hyper-V";
319 case 0x00000027: return "Server Datacenter without Hyper-V (Core)";
320 case 0x00000028: return "Server Standard without Hyper-V (Core)";
321 case 0x00000029: return "Server Enterprise without Hyper-V (Core)";
322 case 0x0000002A: return "Hyper-V Server";
323 case 0x0000002F: return "Starter N";
324 case 0x00000030: return "Pro";
325 case 0x00000031: return "Pro N";
326 case 0x00000034: return "Server Solutions Premium";
327 case 0x00000035: return "Server Solutions Premium (Core)";
328 case 0x00000040: return "Server Hyper Core V";
329 case 0x00000042: return "Starter E";
330 case 0x00000043: return "Home Basic E";
331 case 0x00000044: return "Premium E";
332 case 0x00000045: return "Pro E";
333 case 0x00000046: return "Enterprise E";
334 case 0x00000047: return "Ultimate E";
335 case 0x00000048: return "Enterprise (Eval)";
336 case 0x0000004F: return "Server Standard (Eval)";
337 case 0x00000050: return "Server Datacenter (Eval)";
338 case 0x00000054: return "Enterprise N (Eval)";
339 case 0x00000057: return "Thin PC";
340 case 0x00000058: case 0x00000059: case 0x0000005A: case 0x0000005B: case 0x0000005C: return "Embedded";
341 case 0x00000062: return "Home N";
342 case 0x00000063: return "Home China";
343 case 0x00000064: return "Home Single Language";
344 case 0x00000065: return "Home";
345 case 0x00000067: return "Pro with Media Center";
346 case 0x00000069: case 0x0000006A: case 0x0000006B: case 0x0000006C: return "Embedded";
347 case 0x0000006F: return "Home Connected";
348 case 0x00000070: return "Pro Student";
349 case 0x00000071: return "Home Connected N";
350 case 0x00000072: return "Pro Student N";
351 case 0x00000073: return "Home Connected Single Language";
352 case 0x00000074: return "Home Connected China";
353 case 0x00000079: return "Education";
354 case 0x0000007A: return "Education N";
355 case 0x0000007D: return "Enterprise LTSB";
356 case 0x0000007E: return "Enterprise LTSB N";
357 case 0x0000007F: return "Pro S";
358 case 0x00000080: return "Pro S N";
359 case 0x00000081: return "Enterprise LTSB (Eval)";
360 case 0x00000082: return "Enterprise LTSB N (Eval)";
361 case 0x0000008A: return "Pro Single Language";
362 case 0x0000008B: return "Pro China";
363 case 0x0000008C: return "Enterprise Subscription";
364 case 0x0000008D: return "Enterprise Subscription N";
365 case 0x00000091: return "Server Datacenter SA (Core)";
366 case 0x00000092: return "Server Standard SA (Core)";
367 case 0x00000095: return "Utility VM";
368 case 0x000000A1: return "Pro for Workstations";
369 case 0x000000A2: return "Pro for Workstations N";
370 case 0x000000A4: return "Pro for Education";
371 case 0x000000A5: return "Pro for Education N";
372 case 0x000000AB: return "Enterprise G"; // I swear Microsoft are just making up editions...
373 case 0x000000AC: return "Enterprise G N";
374 case 0x000000B6: return "Home OS";
375 case 0x000000B7: return "Cloud E";
376 case 0x000000B8: return "Cloud E N";
377 case 0x000000BD: return "Lite";
378 case 0xABCDABCD: return "(Unlicensed)";
379 default: return "(Unknown Edition)";
380 }
381 }
382
383 #define is_x64 IsWow64
384 #define static_strcpy safe_strcpy
385 #define REGKEY_HKCU HKEY_CURRENT_USER
386 #define REGKEY_HKLM HKEY_LOCAL_MACHINE
387 static int nWindowsVersion = WINDOWS_UNDEFINED;
388 static int nWindowsBuildNumber = -1;
389 static char WindowsVersionStr[128] = "";
390
391 /* Helpers for 32 bit registry operations */
392
393 /*
394 * Read a generic registry key value. If a short key_name is used, assume that
395 * it belongs to the application and create the app subkey if required
396 */
397 static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type,
398 LPBYTE dest, DWORD dest_size)
399 {
400 const char software_prefix[] = "SOFTWARE\\";
401 char long_key_name[MAX_PATH] = { 0 };
402 BOOL r = FALSE;
403 size_t i;
404 LONG s;
405 HKEY hSoftware = NULL, hApp = NULL;
406 DWORD dwType = -1, dwSize = dest_size;
407
408 memset(dest, 0, dest_size);
409
410 if (key_name == NULL)
411 return FALSE;
412
413 for (i = strlen(key_name); i>0; i--) {
414 if (key_name[i] == '\\')
415 break;
416 }
417
418 if (i > 0) {
419 // Prefix with "SOFTWARE" if needed
420 if (_strnicmp(key_name, software_prefix, sizeof(software_prefix)-1) != 0) {
421 if (i + sizeof(software_prefix) >= sizeof(long_key_name))
422 return FALSE;
423 strcpy_s(long_key_name, sizeof(long_key_name), software_prefix);
424 strcat_s(long_key_name, sizeof(long_key_name), key_name);
425 long_key_name[sizeof(software_prefix)+i - 1] = 0;
426 }
427 else {
428 if (i >= sizeof(long_key_name))
429 return FALSE;
430 static_strcpy(long_key_name, key_name);
431 long_key_name[i] = 0;
432 }
433 i++;
434 if (RegOpenKeyExA(key_root, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) {
435 hApp = NULL;
436 goto out;
437 }
438 }
439 else {
440 if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ | KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) {
441 hSoftware = NULL;
442 goto out;
443 }
444 }
445
446 s = RegQueryValueExA(hApp, &key_name[i], NULL, &dwType, (LPBYTE)dest, &dwSize);
447 // No key means default value of 0 or empty string
448 if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType == reg_type) && (dwSize > 0))) {
449 r = TRUE;
450 }
451 out:
452 if (hSoftware != NULL)
453 RegCloseKey(hSoftware);
454 if (hApp != NULL)
455 RegCloseKey(hApp);
456 return r;
457 }
458
459 #define GetRegistryKey32(root, key, pval) _GetRegistryKey(root, key, REG_DWORD, (LPBYTE)pval, sizeof(DWORD))
460 static __inline INT32 ReadRegistryKey32(HKEY root, const char* key) {
461 DWORD val;
462 GetRegistryKey32(root, key, &val);
463 return (INT32)val;
464 }
465
466 /*
467 * Modified from smartmontools' os_win32.cpp
468 */
469 void GetWindowsVersion(void)
470 {
471 OSVERSIONINFOEXA vi, vi2;
472 DWORD dwProductType;
473 const char* w = 0;
474 const char* w64 = "32 bit";
475 char *vptr;
476 size_t vlen;
477 unsigned major, minor;
478 ULONGLONG major_equal, minor_equal;
479 BOOL ws;
480
481 nWindowsVersion = WINDOWS_UNDEFINED;
482 static_strcpy(WindowsVersionStr, "Windows Undefined");
483
484 // suppress the C4996 warning for GetVersionExA
485 #pragma warning(push)
486 #pragma warning(disable:4996)
487
488 memset(&vi, 0, sizeof(vi));
489 vi.dwOSVersionInfoSize = sizeof(vi);
490 if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
491 memset(&vi, 0, sizeof(vi));
492 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
493 if (!GetVersionExA((OSVERSIONINFOA *)&vi))
494 return;
495 }
496
497 #pragma warning(pop)
498
499 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
500
501 if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
502 // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
503 // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
504 // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS
505 // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor...
506
507 major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
508 for (major = vi.dwMajorVersion; major <= 9; major++) {
509 memset(&vi2, 0, sizeof(vi2));
510 vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;
511 if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
512 continue;
513 if (vi.dwMajorVersion < major) {
514 vi.dwMajorVersion = major; vi.dwMinorVersion = 0;
515 }
516
517 minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
518 for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
519 memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);
520 vi2.dwMinorVersion = minor;
521 if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
522 continue;
523 vi.dwMinorVersion = minor;
524 break;
525 }
526
527 break;
528 }
529 }
530
531 if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
532 ws = (vi.wProductType <= VER_NT_WORKSTATION);
533 nWindowsVersion = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
534 switch (nWindowsVersion) {
535 case WINDOWS_XP: w = "XP";
536 break;
537 case WINDOWS_2003: w = (ws ? "XP_64" : (!GetSystemMetrics(89) ? "Server 2003" : "Server 2003_R2"));
538 break;
539 case WINDOWS_VISTA: w = (ws ? "Vista" : "Server 2008");
540 break;
541 case WINDOWS_7: w = (ws ? "7" : "Server 2008_R2");
542 break;
543 case WINDOWS_8: w = (ws ? "8" : "Server 2012");
544 break;
545 case WINDOWS_8_1: w = (ws ? "8.1" : "Server 2012_R2");
546 break;
547 case WINDOWS_10_PREVIEW1: w = (ws ? "10 (Preview 1)" : "Server 10 (Preview 1)");
548 break;
549 // Starting with Windows 10 Preview 2, the major is the same as the public-facing version
550 case WINDOWS_10:
551 if (vi.dwBuildNumber < 20000) {
552 w = (ws ? "10" : ((vi.dwBuildNumber < 17763) ? "Server 2016" : "Server 2019"));
553 break;
554 }
555 nWindowsVersion = WINDOWS_11;
556 // Fall through
557 case WINDOWS_11: w = (ws ? "11" : "Server 2022");
558 break;
559 default:
560 if (nWindowsVersion < WINDOWS_XP)
561 nWindowsVersion = WINDOWS_UNSUPPORTED;
562 else
563 w = "12 or later";
564 break;
565 }
566 }
567 }
568
569 if (is_x64())
570 w64 = "64-bit";
571
572 GetProductInfo(vi.dwMajorVersion, vi.dwMinorVersion, vi.wServicePackMajor, vi.wServicePackMinor, &dwProductType);
573 vptr = WindowsVersionStr;
574 vlen = sizeof(WindowsVersionStr) - 1;
575
576 if (!w)
577 sprintf_s(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"),
578 (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
579 else if (vi.wServicePackMinor)
580 sprintf_s(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
581 else if (vi.wServicePackMajor)
582 sprintf_s(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64);
583 else
584 sprintf_s(vptr, vlen, "%s%s%s, %s",
585 w, (dwProductType != PRODUCT_UNDEFINED) ? " " : "", GetEdition(dwProductType), w64);
586
587 // Add the build number (including UBR if available) for Windows 8.0 and later
588 nWindowsBuildNumber = vi.dwBuildNumber;
589 if (nWindowsVersion >= 0x62) {
590 int nUbr = ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows NT\\CurrentVersion\\UBR");
591 vptr = WindowsVersionStr + strlen(WindowsVersionStr);
592 vlen = sizeof(WindowsVersionStr) - strlen(WindowsVersionStr) - 1;
593 if (nUbr > 0)
594 sprintf_s(vptr, vlen, " (Build %d.%d)", nWindowsBuildNumber, nUbr);
595 else
596 sprintf_s(vptr, vlen, " (Build %d)", nWindowsBuildNumber);
597 }
598 }
599
600
601
602 void DumpWindowsVersion(void)
603 {
604 GetWindowsVersion();
605 Log("Windows Version: <<Windows %s>>", WindowsVersionStr);
606 return;
607 }
608
609
610 BOOL IsVentoyLogicalDrive(CHAR DriveLetter)
611 {
612 int i;
613 CONST CHAR *Files[] =
614 {
615 "EFI\\BOOT\\BOOTX64.EFI",
616 "grub\\themes\\ventoy\\theme.txt",
617 "ventoy\\ventoy.cpio",
618 };
619
620 for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)
621 {
622 if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))
623 {
624 return FALSE;
625 }
626 }
627
628 return TRUE;
629 }
630
631
632 int VentoyFillMBRLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
633 {
634 BYTE Head;
635 BYTE Sector;
636 BYTE nSector = 63;
637 BYTE nHead = 8;
638 UINT32 Cylinder;
639 UINT32 EndSectorId;
640
641 while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
642 {
643 nHead = (BYTE)nHead * 2;
644 }
645
646 if (nHead == 0)
647 {
648 nHead = 255;
649 }
650
651 Cylinder = StartSectorId / nSector / nHead;
652 Head = StartSectorId / nSector % nHead;
653 Sector = StartSectorId % nSector + 1;
654
655 Table->StartHead = Head;
656 Table->StartSector = Sector;
657 Table->StartCylinder = Cylinder;
658
659 EndSectorId = StartSectorId + SectorCount - 1;
660 Cylinder = EndSectorId / nSector / nHead;
661 Head = EndSectorId / nSector % nHead;
662 Sector = EndSectorId % nSector + 1;
663
664 Table->EndHead = Head;
665 Table->EndSector = Sector;
666 Table->EndCylinder = Cylinder;
667
668 Table->StartSectorId = StartSectorId;
669 Table->SectorCount = SectorCount;
670
671 return 0;
672 }
673
674 int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR, int PartStyle, UINT8 FsFlag)
675 {
676 GUID Guid;
677 int ReservedValue;
678 UINT32 DiskSignature;
679 UINT32 DiskSectorCount;
680 UINT32 PartSectorCount;
681 UINT32 PartStartSector;
682 UINT32 ReservedSector;
683
684 VentoyGetLocalBootImg(pMBR);
685
686 CoCreateGuid(&Guid);
687
688 memcpy(&DiskSignature, &Guid, sizeof(UINT32));
689
690 Log("Disk signature: 0x%08x", DiskSignature);
691
692 *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;
693 memcpy(pMBR->BootCode + 0x180, &Guid, 16);
694
695 if (DiskSizeBytes / 512 > 0xFFFFFFFF)
696 {
697 DiskSectorCount = 0xFFFFFFFF;
698 }
699 else
700 {
701 DiskSectorCount = (UINT32)(DiskSizeBytes / 512);
702 }
703
704 ReservedValue = GetReservedSpaceInMB();
705 if (ReservedValue <= 0)
706 {
707 ReservedSector = 0;
708 }
709 else
710 {
711 ReservedSector = (UINT32)(ReservedValue * 2048);
712 }
713
714 if (PartStyle)
715 {
716 ReservedSector += 33; // backup GPT part table
717 }
718
719 // check aligned with 4KB
720 if (IsPartNeed4KBAlign())
721 {
722 UINT64 sectors = DiskSizeBytes / 512;
723 if (sectors % 8)
724 {
725 Log("Disk need to align with 4KB %u", (UINT32)(sectors % 8));
726 ReservedSector += (UINT32)(sectors % 8);
727 }
728 }
729
730 Log("ReservedSector: %u", ReservedSector);
731
732 //Part1
733 PartStartSector = VENTOY_PART1_START_SECTOR;
734 PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;
735 VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);
736
737 pMBR->PartTbl[0].Active = 0x80; // bootable
738 pMBR->PartTbl[0].FsFlag = FsFlag; // File system flag 07:exFAT/NTFS/HPFS 0C:FAT32
739
740 //Part2
741 PartStartSector += PartSectorCount;
742 PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
743 VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
744
745 pMBR->PartTbl[1].Active = 0x00;
746 pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
747
748 pMBR->Byte55 = 0x55;
749 pMBR->ByteAA = 0xAA;
750
751 return 0;
752 }
753
754
755 static int VentoyFillProtectMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)
756 {
757 GUID Guid;
758 UINT32 DiskSignature;
759 UINT64 DiskSectorCount;
760
761 VentoyGetLocalBootImg(pMBR);
762
763 CoCreateGuid(&Guid);
764
765 memcpy(&DiskSignature, &Guid, sizeof(UINT32));
766
767 Log("Disk signature: 0x%08x", DiskSignature);
768
769 *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;
770 memcpy(pMBR->BootCode + 0x180, &Guid, 16);
771
772 DiskSectorCount = DiskSizeBytes / 512 - 1;
773 if (DiskSectorCount > 0xFFFFFFFF)
774 {
775 DiskSectorCount = 0xFFFFFFFF;
776 }
777
778 memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));
779
780 pMBR->PartTbl[0].Active = 0x00;
781 pMBR->PartTbl[0].FsFlag = 0xee; // EE
782
783 pMBR->PartTbl[0].StartHead = 0;
784 pMBR->PartTbl[0].StartSector = 1;
785 pMBR->PartTbl[0].StartCylinder = 0;
786 pMBR->PartTbl[0].EndHead = 254;
787 pMBR->PartTbl[0].EndSector = 63;
788 pMBR->PartTbl[0].EndCylinder = 1023;
789
790 pMBR->PartTbl[0].StartSectorId = 1;
791 pMBR->PartTbl[0].SectorCount = (UINT32)DiskSectorCount;
792
793 pMBR->Byte55 = 0x55;
794 pMBR->ByteAA = 0xAA;
795
796 pMBR->BootCode[92] = 0x22;
797
798 return 0;
799 }
800
801 int VentoyFillWholeGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)
802 {
803 UINT64 Part1SectorCount = 0;
804 UINT64 DiskSectorCount = DiskSizeBytes / 512;
805 VTOY_GPT_HDR *Head = &pInfo->Head;
806 VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;
807 static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
808
809 VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);
810
811 Part1SectorCount = DiskSectorCount - 33 - 2048;
812
813 memcpy(Head->Signature, "EFI PART", 8);
814 Head->Version[2] = 0x01;
815 Head->Length = 92;
816 Head->Crc = 0;
817 Head->EfiStartLBA = 1;
818 Head->EfiBackupLBA = DiskSectorCount - 1;
819 Head->PartAreaStartLBA = 34;
820 Head->PartAreaEndLBA = DiskSectorCount - 34;
821 CoCreateGuid(&Head->DiskGuid);
822 Head->PartTblStartLBA = 2;
823 Head->PartTblTotNum = 128;
824 Head->PartTblEntryLen = 128;
825
826
827 memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));
828 CoCreateGuid(&(Table[0].PartGuid));
829 Table[0].StartLBA = 2048;
830 Table[0].LastLBA = 2048 + Part1SectorCount - 1;
831 Table[0].Attr = 0;
832 memcpy(Table[0].Name, L"Data", 4 * 2);
833
834 //Update CRC
835 Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));
836 Head->Crc = VentoyCrc32(Head, Head->Length);
837
838 return 0;
839 }
840
841 int VentoyFillGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)
842 {
843 INT64 ReservedValue = 0;
844 UINT64 ModSectorCount = 0;
845 UINT64 ReservedSector = 33;
846 UINT64 Part1SectorCount = 0;
847 UINT64 DiskSectorCount = DiskSizeBytes / 512;
848 VTOY_GPT_HDR *Head = &pInfo->Head;
849 VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;
850 static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
851 static GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
852 static GUID BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
853
854 VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);
855
856 ReservedValue = GetReservedSpaceInMB();
857 if (ReservedValue > 0)
858 {
859 ReservedSector += ReservedValue * 2048;
860 }
861
862 Part1SectorCount = DiskSectorCount - ReservedSector - (VENTOY_EFI_PART_SIZE / 512) - 2048;
863
864 ModSectorCount = (Part1SectorCount % 8);
865 if (ModSectorCount)
866 {
867 Log("Part1SectorCount:%llu is not aligned by 4KB (%llu)", (ULONGLONG)Part1SectorCount, (ULONGLONG)ModSectorCount);
868 }
869
870 // check aligned with 4KB
871 if (IsPartNeed4KBAlign())
872 {
873 if (ModSectorCount)
874 {
875 Log("Disk need to align with 4KB %u", (UINT32)ModSectorCount);
876 Part1SectorCount -= ModSectorCount;
877 }
878 else
879 {
880 Log("no need to align with 4KB");
881 }
882 }
883
884 memcpy(Head->Signature, "EFI PART", 8);
885 Head->Version[2] = 0x01;
886 Head->Length = 92;
887 Head->Crc = 0;
888 Head->EfiStartLBA = 1;
889 Head->EfiBackupLBA = DiskSectorCount - 1;
890 Head->PartAreaStartLBA = 34;
891 Head->PartAreaEndLBA = DiskSectorCount - 34;
892 CoCreateGuid(&Head->DiskGuid);
893 Head->PartTblStartLBA = 2;
894 Head->PartTblTotNum = 128;
895 Head->PartTblEntryLen = 128;
896
897
898 memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));
899 CoCreateGuid(&(Table[0].PartGuid));
900 Table[0].StartLBA = 2048;
901 Table[0].LastLBA = 2048 + Part1SectorCount - 1;
902 Table[0].Attr = 0;
903 memcpy(Table[0].Name, L"Ventoy", 6 * 2);
904
905 // to fix windows issue
906 //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
907 memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(GUID));
908 CoCreateGuid(&(Table[1].PartGuid));
909 Table[1].StartLBA = Table[0].LastLBA + 1;
910 Table[1].LastLBA = Table[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1;
911 Table[1].Attr = 0xC000000000000001ULL;
912 memcpy(Table[1].Name, L"VTOYEFI", 7 * 2);
913
914 #if 0
915 memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(GUID));
916 CoCreateGuid(&(Table[2].PartGuid));
917 Table[2].StartLBA = 34;
918 Table[2].LastLBA = 2047;
919 Table[2].Attr = 0;
920 #endif
921
922 //Update CRC
923 Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));
924 Head->Crc = VentoyCrc32(Head, Head->Length);
925
926 return 0;
927 }
928
929 int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
930 {
931 UINT64 LBA;
932 UINT64 BackupLBA;
933
934 memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
935
936 LBA = pHead->EfiStartLBA;
937 BackupLBA = pHead->EfiBackupLBA;
938
939 pHead->EfiStartLBA = BackupLBA;
940 pHead->EfiBackupLBA = LBA;
941 pHead->PartTblStartLBA = BackupLBA + 1 - 33;
942
943 pHead->Crc = 0;
944 pHead->Crc = VentoyCrc32(pHead, pHead->Length);
945
946 return 0;
947 }
948
949 CHAR GetFirstUnusedDriveLetter(void)
950 {
951 CHAR Letter = 'D';
952 DWORD Drives = GetLogicalDrives();
953
954 Drives >>= 3;
955 while (Drives & 0x1)
956 {
957 Letter++;
958 Drives >>= 1;
959 }
960
961 return Letter;
962 }
963
964 const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)
965 {
966 switch (Type)
967 {
968 case BusTypeUnknown: return "unknown";
969 case BusTypeScsi: return "SCSI";
970 case BusTypeAtapi: return "Atapi";
971 case BusTypeAta: return "ATA";
972 case BusType1394: return "1394";
973 case BusTypeSsa: return "SSA";
974 case BusTypeFibre: return "Fibre";
975 case BusTypeUsb: return "USB";
976 case BusTypeRAID: return "RAID";
977 case BusTypeiScsi: return "iSCSI";
978 case BusTypeSas: return "SAS";
979 case BusTypeSata: return "SATA";
980 case BusTypeSd: return "SD";
981 case BusTypeMmc: return "MMC";
982 case BusTypeVirtual: return "Virtual";
983 case BusTypeFileBackedVirtual: return "FileBackedVirtual";
984 case BusTypeSpaces: return "Spaces";
985 case BusTypeNvme: return "Nvme";
986 }
987 return "unknown";
988 }
989
990 int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
991 {
992 int Len = 0;
993 BYTE *ImgBuf = NULL;
994 static int Loaded = 0;
995 static MBR_HEAD MBR;
996
997 if (Loaded)
998 {
999 memcpy(pMBR, &MBR, 512);
1000 return 0;
1001 }
1002
1003 if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))
1004 {
1005 Log("Copy boot img success");
1006 memcpy(pMBR, ImgBuf, 512);
1007 free(ImgBuf);
1008
1009 CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));
1010
1011 memcpy(&MBR, pMBR, 512);
1012 Loaded = 1;
1013
1014 return 0;
1015 }
1016 else
1017 {
1018 Log("Copy boot img failed");
1019 return 1;
1020 }
1021 }
1022
1023 int GetHumanReadableGBSize(UINT64 SizeBytes)
1024 {
1025 int i;
1026 int Pow2 = 1;
1027 double Delta;
1028 double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
1029
1030 if ((SizeBytes % 1073741824) == 0)
1031 {
1032 return (int)(SizeBytes / 1073741824);
1033 }
1034
1035 for (i = 0; i < 12; i++)
1036 {
1037 if (Pow2 > GB)
1038 {
1039 Delta = (Pow2 - GB) / Pow2;
1040 }
1041 else
1042 {
1043 Delta = (GB - Pow2) / Pow2;
1044 }
1045
1046 if (Delta < 0.05)
1047 {
1048 return Pow2;
1049 }
1050
1051 Pow2 <<= 1;
1052 }
1053
1054 return (int)GB;
1055 }
1056
1057 void TrimString(CHAR *String)
1058 {
1059 CHAR *Pos1 = String;
1060 CHAR *Pos2 = String;
1061 size_t Len = strlen(String);
1062
1063 while (Len > 0)
1064 {
1065 if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
1066 {
1067 break;
1068 }
1069 String[Len - 1] = 0;
1070 Len--;
1071 }
1072
1073 while (*Pos1 == ' ' || *Pos1 == '\t')
1074 {
1075 Pos1++;
1076 }
1077
1078 while (*Pos1)
1079 {
1080 *Pos2++ = *Pos1++;
1081 }
1082 *Pos2++ = 0;
1083
1084 return;
1085 }
1086
1087 int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)
1088 {
1089 HKEY hKey;
1090 DWORD Type;
1091 DWORD Size;
1092 LSTATUS lRet;
1093 DWORD Value;
1094
1095 lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);
1096 Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);
1097
1098 if (ERROR_SUCCESS == lRet)
1099 {
1100 Size = sizeof(Value);
1101 lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);
1102 Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value);
1103
1104 *pValue = Value;
1105 RegCloseKey(hKey);
1106
1107 return 0;
1108 }
1109 else
1110 {
1111 return 1;
1112 }
1113 }
1114
1115 int GetPhysicalDriveCount(void)
1116 {
1117 DWORD Value;
1118 int Count = 0;
1119
1120 if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)
1121 {
1122 Count = (int)Value;
1123 }
1124
1125 Log("GetPhysicalDriveCount: %d", Count);
1126 return Count;
1127 }
1128
1129 void VentoyStringToUpper(CHAR* str)
1130 {
1131 while (str && *str)
1132 {
1133 if (*str >= 'a' && *str <= 'z')
1134 {
1135 *str = toupper(*str);
1136 }
1137 str++;
1138 }
1139 }