10 static WCHAR g_CurDirW
[MAX_PATH
];
11 static CHAR g_CurDirA
[MAX_PATH
];
12 static CHAR g_LogFile
[MAX_PATH
];
13 static HWND g_create_button
;
14 static HWND g_parse_button
;
16 static BOOL g_ShowHelp
= FALSE
;
17 static WCHAR g_CmdInFile
[MAX_PATH
];
18 static WCHAR g_CmdOutFile
[MAX_PATH
];
27 MSGID_SRC_UNSUPPORTED
,
29 MSGID_SUFFIX_UNSUPPORTED
,
33 MSGID_CREATE_FILE_ERR
,
38 MSGID_FILE_NAME_TOO_LONG
,
44 const WCHAR
*g_msg_cn
[MSGID_BUTT
] =
51 L
"²»Ö§³ÖΪ´ËÎļþ´´½¨vlnk",
53 L
"²»Ö§³ÖµÄÎļþºó׺Ãû",
54 L
"»ñÈ¡´ÅÅÌÐÅϢʱ·¢Éú´íÎó",
55 L
"Vlnk Îļþ´´½¨³É¹¦¡£",
56 L
"ÇëÏȹرÕÕýÔÚÔËÐÐµÄ VentoyVlnk ³ÌÐò£¡",
58 L
"´ËÎļþÒѾÊÇÒ»¸övlnkÎļþÁË£¡",
61 L
"´Ë vlnk Ö¸ÏòµÄÎļþ²»´æÔÚ£¡",
64 const WCHAR
*g_msg_en
[MSGID_BUTT
] =
70 L
"The specified file is not exist!",
71 L
"This file is not supported for vlnk",
72 L
"Unsupported file system!",
73 L
"Unsupported file suffix!",
74 L
"Error when getting disk info",
75 L
"Vlnk file successfully created!",
76 L
"Please close another running VentoyVlnk instance!",
77 L
"Failed to create file!",
78 L
"This file is already a vlnk file!",
79 L
"Invalid vlnk file!",
80 L
"The vlnk file point to ",
81 L
"The file pointed by the vlnk does NOT exist!",
82 L
"The file full path is too long!",
85 const WCHAR
**g_msg_lang
= NULL
;
89 static int VtoyMessageBox
92 _In_opt_ LPCWSTR lpText
,
93 _In_opt_ LPCWSTR lpCaption
,
97 if (g_CmdInFile
[0] && g_CmdOutFile
[0])
102 return MessageBox(hWnd
, lpText
, lpCaption
, uType
);
105 static void Log2File(const char *log
)
112 localtime_s(&ttm
, &stamp
);
114 fopen_s(&fp
, g_LogFile
, "a+");
117 fprintf_s(fp
, "[%04u/%02u/%02u %02u:%02u:%02u] %s",
118 ttm
.tm_year
+ 1900, ttm
.tm_mon
+ 1, ttm
.tm_mday
,
119 ttm
.tm_hour
, ttm
.tm_min
, ttm
.tm_sec
, log
);
124 void LogW(const WCHAR
*Fmt
, ...)
130 if (g_LogFile
[0] == 0)
136 vswprintf_s(log
, 512, Fmt
, arg
);
139 WideCharToMultiByte(CP_UTF8
, 0, log
, -1, alog
, 2048, 0, 0);
145 void LogA(const CHAR
*Fmt
, ...)
150 if (g_LogFile
[0] == 0)
156 vsprintf_s(log
, 512, Fmt
, arg
);
162 static int Utf8ToUtf16(const char* src
, WCHAR
* dst
)
164 int size
= MultiByteToWideChar(CP_UTF8
, 0, src
, -1, dst
, 0);
165 return MultiByteToWideChar(CP_UTF8
, 0, src
, -1, dst
, size
+ 1);
168 static BOOL
OnDestroyDialog()
174 static BOOL
InitDialog(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
178 g_create_button
= GetDlgItem(hWnd
, IDC_BUTTON1
);
179 g_parse_button
= GetDlgItem(hWnd
, IDC_BUTTON2
);
181 hIcon
= LoadIcon(g_hInst
, MAKEINTRESOURCE(IDI_ICON1
));
182 SendMessage(hWnd
, WM_SETICON
, ICON_BIG
, (LPARAM
)hIcon
);
183 SendMessage(hWnd
, WM_SETICON
, ICON_SMALL
, (LPARAM
)hIcon
);
185 SetWindowTextW(g_create_button
, g_msg_lang
[MSGID_BTN_CREATE
]);
186 SetWindowTextW(g_parse_button
, g_msg_lang
[MSGID_BTN_PARSE
]);
191 static int GetPhyDiskInfo(const char LogicalDrive
, UINT32
*DiskSig
, DISK_EXTENT
*DiskExtent
)
196 VOLUME_DISK_EXTENTS DiskExtents
;
198 UINT8 SectorBuf
[512];
200 LogA("GetPhyDiskInfo %C\n", LogicalDrive
);
202 sprintf_s(PhyPath
, sizeof(PhyPath
), "\\\\.\\%C:", LogicalDrive
);
203 Handle
= CreateFileA(PhyPath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
204 if (Handle
== INVALID_HANDLE_VALUE
)
206 LogA("Could not open the disk %C: error:%u\n", LogicalDrive
, GetLastError());
210 Ret
= DeviceIoControl(Handle
,
211 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
,
215 (DWORD
)(sizeof(DiskExtents
)),
218 if (!Ret
|| DiskExtents
.NumberOfDiskExtents
== 0)
220 LogA("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, error:%u\n", GetLastError());
226 memcpy(DiskExtent
, DiskExtents
.Extents
, sizeof(DISK_EXTENT
));
227 LogA("%C: is in PhysicalDrive%d Offset:%llu\n", LogicalDrive
, DiskExtents
.Extents
[0].DiskNumber
,
228 (ULONGLONG
)(DiskExtents
.Extents
[0].StartingOffset
.QuadPart
));
230 sprintf_s(PhyPath
, sizeof(PhyPath
), "\\\\.\\PhysicalDrive%d", DiskExtents
.Extents
[0].DiskNumber
);
231 Handle
= CreateFileA(PhyPath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
232 if (Handle
== INVALID_HANDLE_VALUE
)
234 LogA("Could not open the disk<PhysicalDrive%d>, error:%u\n", DiskExtents
.Extents
[0].DiskNumber
, GetLastError());
238 if (!ReadFile(Handle
, SectorBuf
, sizeof(SectorBuf
), &dwSize
, NULL
))
240 LogA("ReadFile failed, dwSize:%u error:%u\n", dwSize
, GetLastError());
245 memcpy(DiskSig
, SectorBuf
+ 0x1B8, 4);
252 static int SaveBuffer2File(const WCHAR
*Fullpath
, void *Buffer
, DWORD Length
)
258 LogW(L
"SaveBuffer2File <%ls> len:%u\n", Fullpath
, Length
);
260 Handle
= CreateFileW(Fullpath
, GENERIC_READ
| GENERIC_WRITE
,
261 FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, CREATE_ALWAYS
, 0, 0);
262 if (Handle
== INVALID_HANDLE_VALUE
)
264 LogA("Could not create new file, error:%u\n", GetLastError());
268 WriteFile(Handle
, Buffer
, Length
, &dwSize
, NULL
);
274 if (Handle
!= INVALID_HANDLE_VALUE
)
283 static int DefaultVlnkDstFullPath(WCHAR
*Src
, WCHAR
*Dir
, WCHAR
*Dst
)
290 len
= (int)lstrlen(Src
);
291 for (i
= len
- 1; i
>= 0; i
--)
297 wrlen
= swprintf_s(Dst
, MAX_PATH
, L
"%ls\\%ls.vlnk.%ls", Dir
, Src
, Src
+ i
+ 1);
300 for (j
= wrlen
- (len
- i
); j
< wrlen
; j
++)
302 if (Dst
[j
] >= 'A' && Dst
[j
] <= 'Z')
304 Dst
[j
] = 'a' + (Dst
[j
] - 'A');
315 static BOOL
IsVlnkFile(WCHAR
*path
, ventoy_vlnk
*outvlnk
)
320 LARGE_INTEGER FileSize
;
324 Handle
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
325 if (Handle
== INVALID_HANDLE_VALUE
)
327 LogA("Could not open this file, error:%u\n", GetLastError());
331 if (!GetFileSizeEx(Handle
, &FileSize
))
333 LogA("Failed to get vlnk file size\n");
337 if (FileSize
.QuadPart
!= VLNK_FILE_LEN
)
339 LogA("Invalid vlnk file length %llu\n", (unsigned long long)FileSize
.QuadPart
);
343 memset(&vlnk
, 0, sizeof(vlnk
));
344 bRet
= ReadFile(Handle
, &vlnk
, sizeof(vlnk
), &dwSize
, NULL
);
345 if (bRet
&& CheckVlnkData(&vlnk
))
349 memcpy(outvlnk
, &vlnk
, sizeof(vlnk
));
357 if (Handle
!= INVALID_HANDLE_VALUE
)
366 static int CreateVlnk(HWND hWnd
, WCHAR
*Dir
, WCHAR
*InFile
, WCHAR
*OutFile
)
372 DISK_EXTENT DiskExtend
;
373 OPENFILENAME ofn
= { 0 };
374 CHAR UTF8Path
[MAX_PATH
];
375 WCHAR DstFullPath
[MAX_PATH
];
376 WCHAR szFile
[MAX_PATH
] = { 0 };
377 CHAR suffix
[8] = { 0 };
378 CHAR Drive
[8] = { 0 };
379 CHAR FsName
[64] = { 0 };
382 ventoy_vlnk
*vlnk
= NULL
;
386 wcscpy_s(szFile
, MAX_PATH
, InFile
);
390 ofn
.lStructSize
= sizeof(ofn
);
391 ofn
.hwndOwner
= hWnd
;
392 ofn
.lpstrFile
= szFile
;
393 ofn
.nMaxFile
= sizeof(szFile
);
394 ofn
.lpstrFilter
= L
"Vlnk Source File\0*.iso;*.img;*.wim;*.vhd;*.vhdx;*.vtoy;*.efi;*.dat\0";
395 ofn
.nFilterIndex
= 1;
396 ofn
.lpstrFileTitle
= NULL
;
397 ofn
.nMaxFileTitle
= 0;
398 ofn
.lpstrInitialDir
= NULL
;
399 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
401 if (GetOpenFileName(&ofn
) != TRUE
)
407 LogW(L
"Create vlnk for <%ls>\n", szFile
);
409 len
= lstrlen(szFile
);
411 if (len
< 5 || szFile
[0] == '.' || szFile
[1] != ':')
413 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_SRC_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
417 Drive
[0] = (CHAR
)szFile
[0];
420 if (0 == GetVolumeInformationA(Drive
, NULL
, 0, NULL
, NULL
, NULL
, FsName
, sizeof(FsName
) - 1))
422 LogA("GetVolumeInformationA failed %u\n", GetLastError());
426 LogA("Partition filesystem of <%s> is <%s>\n", Drive
, FsName
);
427 if (_stricmp(FsName
, "NTFS") == 0 ||
428 _stricmp(FsName
, "exFAT") == 0 ||
429 _stricmp(FsName
, "FAT") == 0 ||
430 _stricmp(FsName
, "FAT32") == 0 ||
431 _stricmp(FsName
, "FAT16") == 0 ||
432 _stricmp(FsName
, "FAT12") == 0 ||
433 _stricmp(FsName
, "UDF") == 0)
435 LogA("FS Check OK\n");
439 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_FS_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
444 end
= (szFile
[len
- 5] == '.') ? 5 : 4;
445 for (i
= 0; i
< end
; i
++)
447 suffix
[i
] = (CHAR
)szFile
[len
- (end
- i
)];
450 if (!IsSupportedImgSuffix(suffix
))
452 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_SUFFIX_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
456 if (IsVlnkFile(szFile
, NULL
))
458 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_ALREADY_VLNK
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
462 for (i
= 0; i
< MAX_PATH
&& szFile
[i
]; i
++)
464 if (szFile
[i
] == '\\' || szFile
[i
] == '/')
472 LogA("name part not found\n");
475 LogW(L
"File Name is <%ls>\n", Pos
+ 1);
477 memset(UTF8Path
, 0, sizeof(UTF8Path
));
478 WideCharToMultiByte(CP_UTF8
, 0, szFile
+ 2, -1, UTF8Path
, MAX_PATH
, NULL
, 0);
480 for (i
= 0; i
< MAX_PATH
&& UTF8Path
[i
]; i
++)
482 if (UTF8Path
[i
] == '\\')
488 len
= (int)strlen(UTF8Path
);
489 if (len
>= VLNK_NAME_MAX
)
491 LogA("File name length %d overflow\n", len
);
492 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_FILE_NAME_TOO_LONG
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
496 DiskExtend
.StartingOffset
.QuadPart
= 0;
497 if (GetPhyDiskInfo((char)szFile
[0], &DiskSig
, &DiskExtend
))
499 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_DISK_INFO_ERR
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
503 Buf
= malloc(VLNK_FILE_LEN
);
506 memset(Buf
, 0, VLNK_FILE_LEN
);
507 vlnk
= (ventoy_vlnk
*)Buf
;
508 ventoy_create_vlnk(DiskSig
, (uint64_t)DiskExtend
.StartingOffset
.QuadPart
, UTF8Path
, vlnk
);
512 wcscpy_s(DstFullPath
, MAX_PATH
, OutFile
);
516 DefaultVlnkDstFullPath(Pos
+ 1, Dir
, DstFullPath
);
519 LogW(L
"vlnk output file path is <%ls>\n", DstFullPath
);
521 if (SaveBuffer2File(DstFullPath
, Buf
, VLNK_FILE_LEN
) == 0)
525 LogW(L
"Vlnk file create success <%ls>\n", DstFullPath
);
529 swprintf_s(Msg
, 1024, L
"%ls\r\n\r\n%ls", g_msg_lang
[MSGID_VLNK_SUCCESS
], DstFullPath
);
530 VtoyMessageBox(hWnd
, Msg
, g_msg_lang
[MSGID_INFO
], MB_OK
| MB_ICONINFORMATION
);
534 swprintf_s(Msg
, 1024, L
"%ls\r\n\r\n%ls", g_msg_lang
[MSGID_VLNK_SUCCESS
], DstFullPath
+ lstrlen(Dir
) + 1);
535 VtoyMessageBox(hWnd
, Msg
, g_msg_lang
[MSGID_INFO
], MB_OK
| MB_ICONINFORMATION
);
540 LogA("Vlnk file save failed\n");
541 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_CREATE_FILE_ERR
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
550 static CHAR
GetDriveLetter(UINT32 disksig
, UINT64 PartOffset
)
555 DISK_EXTENT DiskExtent
;
558 Drives
= GetLogicalDrives();
559 LogA("Logic Drives: 0x%x", Drives
);
566 DiskExtent
.StartingOffset
.QuadPart
= 0;
567 if (GetPhyDiskInfo(Letter
, &Sig
, &DiskExtent
) == 0)
569 if (Sig
== disksig
&& DiskExtent
.StartingOffset
.QuadPart
== PartOffset
)
584 static int ParseVlnk(HWND hWnd
)
589 OPENFILENAME ofn
= { 0 };
590 WCHAR szFile
[MAX_PATH
] = { 0 };
591 WCHAR szDst
[MAX_PATH
+ 2] = { 0 };
596 ofn
.lStructSize
= sizeof(ofn
);
597 ofn
.hwndOwner
= hWnd
;
598 ofn
.lpstrFile
= szFile
;
599 ofn
.nMaxFile
= sizeof(szFile
);
600 ofn
.lpstrFilter
= L
"Vlnk File\0*.vlnk.iso;*.vlnk.img;*.vlnk.wim;*.vlnk.efi;*.vlnk.vhd;*.vlnk.vhdx;*.vlnk.vtoy;*.vlnk.dat\0";
601 ofn
.nFilterIndex
= 1;
602 ofn
.lpstrFileTitle
= NULL
;
603 ofn
.nMaxFileTitle
= 0;
604 ofn
.lpstrInitialDir
= NULL
;
605 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
607 if (GetOpenFileName(&ofn
) != TRUE
)
612 LogW(L
"Parse vlnk for <%ls>\n", szFile
);
614 if (!IsVlnkFile(szFile
, &vlnk
))
616 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_INVALID_VLNK
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
620 for (i
= 0; i
< sizeof(vlnk
.filepath
) && vlnk
.filepath
[i
]; i
++)
622 if (vlnk
.filepath
[i
] == '.')
624 suffix
= vlnk
.filepath
+ i
;
628 if (!IsSupportedImgSuffix(suffix
))
630 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_SUFFIX_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
634 Utf8ToUtf16(vlnk
.filepath
, szDst
+ 2);
635 for (i
= 2; i
< MAX_PATH
&& szDst
[i
]; i
++)
644 Letter
= GetDriveLetter(vlnk
.disk_signature
, vlnk
.part_offset
);
647 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_VLNK_NO_DST
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
651 szDst
[0] = toupper(Letter
);
653 LogW(L
"vlnk dst is %ls\n", szDst
);
655 hFile
= CreateFileW(szDst
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
656 if (INVALID_HANDLE_VALUE
== hFile
)
658 VtoyMessageBox(hWnd
, g_msg_lang
[MSGID_VLNK_NO_DST
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
663 swprintf_s(Msg
, 1024, L
"%ls %ls", g_msg_lang
[MSGID_VLNK_POINT_TO
], szDst
);
664 VtoyMessageBox(hWnd
, Msg
, g_msg_lang
[MSGID_INFO
], MB_OK
| MB_ICONINFORMATION
);
669 INT_PTR CALLBACK
DialogProc(HWND hWnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
678 NotifyCode
= HIWORD(wParam
);
679 CtrlID
= LOWORD(wParam
);
681 if (NotifyCode
== BN_CLICKED
)
683 if (CtrlID
== IDC_BUTTON1
)
685 EnableWindow(g_create_button
, FALSE
);
686 CreateVlnk(hWnd
, g_CurDirW
, NULL
, NULL
);
687 EnableWindow(g_create_button
, TRUE
);
689 else if (CtrlID
== IDC_BUTTON2
)
691 EnableWindow(g_parse_button
, FALSE
);
693 EnableWindow(g_parse_button
, TRUE
);
700 InitDialog(hWnd
, wParam
, lParam
);
713 static int ParseCmdLine(LPSTR lpCmdLine
)
717 LPWSTR
*lpszArgv
= NULL
;
719 lpszArgv
= CommandLineToArgvW(GetCommandLineW(), &argc
);
721 for (i
= 0; i
< argc
; i
++)
723 if (lstrcmp(lpszArgv
[i
], L
"-q") == 0 || lstrcmp(lpszArgv
[i
], L
"-Q") == 0)
727 else if (lstrcmp(lpszArgv
[i
], L
"-h") == 0 || lstrcmp(lpszArgv
[i
], L
"-H") == 0)
731 else if (lstrcmp(lpszArgv
[i
], L
"-i") == 0 || lstrcmp(lpszArgv
[i
], L
"-I") == 0)
735 wcscpy_s(g_CmdInFile
, MAX_PATH
, lpszArgv
[i
+ 1]);
738 else if (lstrcmp(lpszArgv
[i
], L
"-o") == 0 || lstrcmp(lpszArgv
[i
], L
"-O") == 0)
742 wcscpy_s(g_CmdOutFile
, MAX_PATH
, lpszArgv
[i
+ 1]);
750 int APIENTRY
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, INT nCmdShow
)
755 UNREFERENCED_PARAMETER(hPrevInstance
);
757 if (GetUserDefaultUILanguage() == 0x0804)
759 g_msg_lang
= g_msg_cn
;
763 g_msg_lang
= g_msg_en
;
766 hMutex
= CreateMutexA(NULL
, TRUE
, "VtoyVlnkMUTEX");
767 if ((hMutex
!= NULL
) && (GetLastError() == ERROR_ALREADY_EXISTS
))
769 MessageBoxW(NULL
, g_msg_lang
[MSGID_RUNNING_TIP
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
773 GetCurrentDirectoryA(MAX_PATH
, g_CurDirA
);
774 GetCurrentDirectoryW(MAX_PATH
, g_CurDirW
);
775 sprintf_s(g_LogFile
, sizeof(g_LogFile
), "%s\\VentoyVlnk.log", g_CurDirA
);
777 ParseCmdLine(lpCmdLine
);
783 VtoyMessageBox(NULL
, L
"VentoyVlnk.exe CMD\r\n -i Input file path\r\n -o Output vlnk file path\r\n -q Quite mode (no log)", L
"Tip", MB_OK
);
786 else if (g_CmdInFile
[0] && g_CmdOutFile
[0])
788 LogA("========= VentoyVlnk Cmdline Mode =========\n");
790 dwAttrib
= GetFileAttributesW(g_CmdInFile
);
791 if (dwAttrib
== INVALID_FILE_ATTRIBUTES
|| (dwAttrib
& FILE_ATTRIBUTE_DIRECTORY
))
793 LogW(L
"File <<%ls>> does not exist!\n", g_CmdInFile
);
794 VtoyMessageBox(NULL
, g_msg_lang
[MSGID_SRC_NONEXIST
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
798 return CreateVlnk(NULL
, g_CurDirW
, g_CmdInFile
, g_CmdOutFile
);
802 LogA("========= VentoyVlnk GUI Mode =========\n");
804 DialogBoxA(hInstance
, MAKEINTRESOURCEA(IDD_DIALOG1
), NULL
, DialogProc
);