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
;
22 MSGID_SRC_UNSUPPORTED
,
24 MSGID_SUFFIX_UNSUPPORTED
,
28 MSGID_CREATE_FILE_ERR
,
33 MSGID_FILE_NAME_TOO_LONG
,
39 const WCHAR
*g_msg_cn
[MSGID_BUTT
] =
45 L
"²»Ö§³ÖΪ´ËÎļþ´´½¨vlnk",
47 L
"²»Ö§³ÖµÄÎļþºó׺Ãû",
48 L
"»ñÈ¡´ÅÅÌÐÅϢʱ·¢Éú´íÎó",
49 L
"Vlnk Îļþ´´½¨³É¹¦¡£",
50 L
"ÇëÏȹرÕÕýÔÚÔËÐÐµÄ VentoyVlnk ³ÌÐò£¡",
52 L
"´ËÎļþÒѾÊÇÒ»¸övlnkÎļþÁË£¡",
55 L
"´Ë vlnk Ö¸ÏòµÄÎļþ²»´æÔÚ£¡",
58 const WCHAR
*g_msg_en
[MSGID_BUTT
] =
64 L
"This file is not supported for vlnk",
65 L
"Unsupported file system!",
66 L
"Unsupported file suffix!",
67 L
"Error when getting disk info",
68 L
"Vlnk file successfully created!",
69 L
"Please close another running VentoyVlnk instance!",
70 L
"Failed to create file!",
71 L
"This file is already a vlnk file!",
72 L
"Invalid vlnk file!",
73 L
"The vlnk file point to ",
74 L
"The file pointed by the vlnk does NOT exist!",
75 L
"The file full path is too long!",
78 const WCHAR
**g_msg_lang
= NULL
;
82 static void Log2File(const char *log
)
89 localtime_s(&ttm
, &stamp
);
91 fopen_s(&fp
, g_LogFile
, "a+");
94 fprintf_s(fp
, "[%04u/%02u/%02u %02u:%02u:%02u] %s",
95 ttm
.tm_year
+ 1900, ttm
.tm_mon
+ 1, ttm
.tm_mday
,
96 ttm
.tm_hour
, ttm
.tm_min
, ttm
.tm_sec
, log
);
101 void LogW(const WCHAR
*Fmt
, ...)
107 if (g_LogFile
[0] == 0)
113 vswprintf_s(log
, 512, Fmt
, arg
);
116 WideCharToMultiByte(CP_UTF8
, 0, log
, -1, alog
, 2048, 0, 0);
122 void LogA(const CHAR
*Fmt
, ...)
127 if (g_LogFile
[0] == 0)
133 vsprintf_s(log
, 512, Fmt
, arg
);
139 static int Utf8ToUtf16(const char* src
, WCHAR
* dst
)
141 int size
= MultiByteToWideChar(CP_UTF8
, 0, src
, -1, dst
, 0);
142 return MultiByteToWideChar(CP_UTF8
, 0, src
, -1, dst
, size
+ 1);
145 static BOOL
OnDestroyDialog()
151 static BOOL
InitDialog(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
155 g_create_button
= GetDlgItem(hWnd
, IDC_BUTTON1
);
156 g_parse_button
= GetDlgItem(hWnd
, IDC_BUTTON2
);
158 hIcon
= LoadIcon(g_hInst
, MAKEINTRESOURCE(IDI_ICON1
));
159 SendMessage(hWnd
, WM_SETICON
, ICON_BIG
, (LPARAM
)hIcon
);
160 SendMessage(hWnd
, WM_SETICON
, ICON_SMALL
, (LPARAM
)hIcon
);
162 SetWindowTextW(g_create_button
, g_msg_lang
[MSGID_BTN_CREATE
]);
163 SetWindowTextW(g_parse_button
, g_msg_lang
[MSGID_BTN_PARSE
]);
168 static int GetPhyDiskInfo(const char LogicalDrive
, UINT32
*DiskSig
, DISK_EXTENT
*DiskExtent
)
173 VOLUME_DISK_EXTENTS DiskExtents
;
175 UINT8 SectorBuf
[512];
177 LogA("GetPhyDiskInfo %C\n", LogicalDrive
);
179 sprintf_s(PhyPath
, sizeof(PhyPath
), "\\\\.\\%C:", LogicalDrive
);
180 Handle
= CreateFileA(PhyPath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
181 if (Handle
== INVALID_HANDLE_VALUE
)
183 LogA("Could not open the disk %C: error:%u\n", LogicalDrive
, GetLastError());
187 Ret
= DeviceIoControl(Handle
,
188 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
,
192 (DWORD
)(sizeof(DiskExtents
)),
195 if (!Ret
|| DiskExtents
.NumberOfDiskExtents
== 0)
197 LogA("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, error:%u\n", GetLastError());
203 memcpy(DiskExtent
, DiskExtents
.Extents
, sizeof(DISK_EXTENT
));
204 LogA("%C: is in PhysicalDrive%d Offset:%llu\n", LogicalDrive
, DiskExtents
.Extents
[0].DiskNumber
,
205 (ULONGLONG
)(DiskExtents
.Extents
[0].StartingOffset
.QuadPart
));
207 sprintf_s(PhyPath
, sizeof(PhyPath
), "\\\\.\\PhysicalDrive%d", DiskExtents
.Extents
[0].DiskNumber
);
208 Handle
= CreateFileA(PhyPath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
209 if (Handle
== INVALID_HANDLE_VALUE
)
211 LogA("Could not open the disk<PhysicalDrive%d>, error:%u\n", DiskExtents
.Extents
[0].DiskNumber
, GetLastError());
215 if (!ReadFile(Handle
, SectorBuf
, sizeof(SectorBuf
), &dwSize
, NULL
))
217 LogA("ReadFile failed, dwSize:%u error:%u\n", dwSize
, GetLastError());
222 memcpy(DiskSig
, SectorBuf
+ 0x1B8, 4);
229 static int SaveBuffer2File(const WCHAR
*Fullpath
, void *Buffer
, DWORD Length
)
235 LogW(L
"SaveBuffer2File <%ls> len:%u\n", Fullpath
, Length
);
237 Handle
= CreateFileW(Fullpath
, GENERIC_READ
| GENERIC_WRITE
,
238 FILE_SHARE_READ
| FILE_SHARE_WRITE
, 0, CREATE_ALWAYS
, 0, 0);
239 if (Handle
== INVALID_HANDLE_VALUE
)
241 LogA("Could not create new file, error:%u\n", GetLastError());
245 WriteFile(Handle
, Buffer
, Length
, &dwSize
, NULL
);
251 if (Handle
!= INVALID_HANDLE_VALUE
)
260 static int DefaultVlnkDstFullPath(WCHAR
*Src
, WCHAR
*Dir
, WCHAR
*Dst
)
267 len
= (int)lstrlen(Src
);
268 for (i
= len
- 1; i
>= 0; i
--)
274 wrlen
= swprintf_s(Dst
, MAX_PATH
, L
"%ls\\%ls.vlnk.%ls", Dir
, Src
, Src
+ i
+ 1);
277 for (j
= wrlen
- (len
- i
); j
< wrlen
; j
++)
279 if (Dst
[j
] >= 'A' && Dst
[j
] <= 'Z')
281 Dst
[j
] = 'a' + (Dst
[j
] - 'A');
292 static BOOL
IsVlnkFile(WCHAR
*path
, ventoy_vlnk
*outvlnk
)
297 LARGE_INTEGER FileSize
;
301 Handle
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
302 if (Handle
== INVALID_HANDLE_VALUE
)
304 LogA("Could not open this file, error:%u\n", GetLastError());
308 if (!GetFileSizeEx(Handle
, &FileSize
))
310 LogA("Failed to get vlnk file size\n");
314 if (FileSize
.QuadPart
!= VLNK_FILE_LEN
)
316 LogA("Invalid vlnk file length %llu\n", (unsigned long long)FileSize
.QuadPart
);
320 memset(&vlnk
, 0, sizeof(vlnk
));
321 bRet
= ReadFile(Handle
, &vlnk
, sizeof(vlnk
), &dwSize
, NULL
);
322 if (bRet
&& CheckVlnkData(&vlnk
))
326 memcpy(outvlnk
, &vlnk
, sizeof(vlnk
));
334 if (Handle
!= INVALID_HANDLE_VALUE
)
343 static int CreateVlnk(HWND hWnd
, WCHAR
*Dir
)
349 DISK_EXTENT DiskExtend
;
350 OPENFILENAME ofn
= { 0 };
351 CHAR UTF8Path
[MAX_PATH
];
352 WCHAR DstFullPath
[MAX_PATH
];
353 WCHAR szFile
[MAX_PATH
] = { 0 };
354 CHAR suffix
[8] = { 0 };
355 CHAR Drive
[8] = { 0 };
356 CHAR FsName
[64] = { 0 };
359 ventoy_vlnk
*vlnk
= NULL
;
361 ofn
.lStructSize
= sizeof(ofn
);
362 ofn
.hwndOwner
= hWnd
;
363 ofn
.lpstrFile
= szFile
;
364 ofn
.nMaxFile
= sizeof(szFile
);
365 ofn
.lpstrFilter
= L
"Vlnk Source File\0*.iso;*.img;*.wim;*.vhd;*.vhdx;*.vtoy;*.efi;*.dat\0";
366 ofn
.nFilterIndex
= 1;
367 ofn
.lpstrFileTitle
= NULL
;
368 ofn
.nMaxFileTitle
= 0;
369 ofn
.lpstrInitialDir
= NULL
;
370 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
372 if (GetOpenFileName(&ofn
) != TRUE
)
377 LogW(L
"Create vlnk for <%ls>\n", szFile
);
379 len
= lstrlen(szFile
);
381 if (len
< 5 || szFile
[0] == '.' || szFile
[1] != ':')
383 MessageBox(hWnd
, g_msg_lang
[MSGID_SRC_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
387 Drive
[0] = (CHAR
)szFile
[0];
390 if (0 == GetVolumeInformationA(Drive
, NULL
, 0, NULL
, NULL
, NULL
, FsName
, sizeof(FsName
) - 1))
392 LogA("GetVolumeInformationA failed %u\n", GetLastError());
396 LogA("Partition filesystem of <%s> is <%s>\n", Drive
, FsName
);
397 if (_stricmp(FsName
, "NTFS") == 0 ||
398 _stricmp(FsName
, "exFAT") == 0 ||
399 _stricmp(FsName
, "FAT") == 0 ||
400 _stricmp(FsName
, "FAT32") == 0 ||
401 _stricmp(FsName
, "FAT16") == 0 ||
402 _stricmp(FsName
, "FAT12") == 0 ||
403 _stricmp(FsName
, "UDF") == 0)
405 LogA("FS Check OK\n");
409 MessageBox(hWnd
, g_msg_lang
[MSGID_FS_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
414 end
= (szFile
[len
- 5] == '.') ? 5 : 4;
415 for (i
= 0; i
< end
; i
++)
417 suffix
[i
] = (CHAR
)szFile
[len
- (end
- i
)];
420 if (!IsSupportedImgSuffix(suffix
))
422 MessageBox(hWnd
, g_msg_lang
[MSGID_SUFFIX_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
426 if (IsVlnkFile(szFile
, NULL
))
428 MessageBox(hWnd
, g_msg_lang
[MSGID_ALREADY_VLNK
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
432 for (i
= 0; i
< MAX_PATH
&& szFile
[i
]; i
++)
434 if (szFile
[i
] == '\\' || szFile
[i
] == '/')
442 LogA("name part not found\n");
445 LogW(L
"File Name is <%ls>\n", Pos
+ 1);
447 memset(UTF8Path
, 0, sizeof(UTF8Path
));
448 WideCharToMultiByte(CP_UTF8
, 0, szFile
+ 2, -1, UTF8Path
, MAX_PATH
, NULL
, 0);
450 for (i
= 0; i
< MAX_PATH
&& UTF8Path
[i
]; i
++)
452 if (UTF8Path
[i
] == '\\')
458 len
= (int)strlen(UTF8Path
);
459 if (len
>= VLNK_NAME_MAX
)
461 LogA("File name length %d overflow\n", len
);
462 MessageBox(hWnd
, g_msg_lang
[MSGID_FILE_NAME_TOO_LONG
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
466 DiskExtend
.StartingOffset
.QuadPart
= 0;
467 if (GetPhyDiskInfo((char)szFile
[0], &DiskSig
, &DiskExtend
))
469 MessageBox(hWnd
, g_msg_lang
[MSGID_DISK_INFO_ERR
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
473 Buf
= malloc(VLNK_FILE_LEN
);
476 memset(Buf
, 0, VLNK_FILE_LEN
);
477 vlnk
= (ventoy_vlnk
*)Buf
;
478 ventoy_create_vlnk(DiskSig
, (uint64_t)DiskExtend
.StartingOffset
.QuadPart
, UTF8Path
, vlnk
);
480 DefaultVlnkDstFullPath(Pos
+ 1, Dir
, DstFullPath
);
481 LogW(L
"vlnk output file path is <%ls>\n", DstFullPath
);
483 if (SaveBuffer2File(DstFullPath
, Buf
, VLNK_FILE_LEN
) == 0)
487 swprintf_s(Msg
, 1024, L
"%ls\r\n\r\n%ls", g_msg_lang
[MSGID_VLNK_SUCCESS
], DstFullPath
+ lstrlen(Dir
) + 1);
489 LogW(L
"Vlnk file create success <%ls>\n", DstFullPath
);
490 MessageBox(hWnd
, Msg
, g_msg_lang
[MSGID_INFO
], MB_OK
| MB_ICONINFORMATION
);
494 LogA("Vlnk file save failed\n");
495 MessageBox(hWnd
, g_msg_lang
[MSGID_CREATE_FILE_ERR
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
504 static CHAR
GetDriveLetter(UINT32 disksig
, UINT64 PartOffset
)
509 DISK_EXTENT DiskExtent
;
512 Drives
= GetLogicalDrives();
513 LogA("Logic Drives: 0x%x", Drives
);
520 DiskExtent
.StartingOffset
.QuadPart
= 0;
521 if (GetPhyDiskInfo(Letter
, &Sig
, &DiskExtent
) == 0)
523 if (Sig
== disksig
&& DiskExtent
.StartingOffset
.QuadPart
== PartOffset
)
538 static int ParseVlnk(HWND hWnd
)
543 OPENFILENAME ofn
= { 0 };
544 WCHAR szFile
[MAX_PATH
] = { 0 };
545 WCHAR szDst
[MAX_PATH
+ 2] = { 0 };
550 ofn
.lStructSize
= sizeof(ofn
);
551 ofn
.hwndOwner
= hWnd
;
552 ofn
.lpstrFile
= szFile
;
553 ofn
.nMaxFile
= sizeof(szFile
);
554 ofn
.lpstrFilter
= L
"Vlnk File\0*.vlnk.iso;*.vlnk.img;*.vlnk.wim;*.vlnk.efi;*.vlnk.vhd;*.vlnk.vhdx;*.vlnk.vtoy;*.vlnk.dat\0";
555 ofn
.nFilterIndex
= 1;
556 ofn
.lpstrFileTitle
= NULL
;
557 ofn
.nMaxFileTitle
= 0;
558 ofn
.lpstrInitialDir
= NULL
;
559 ofn
.Flags
= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
561 if (GetOpenFileName(&ofn
) != TRUE
)
566 LogW(L
"Parse vlnk for <%ls>\n", szFile
);
568 if (!IsVlnkFile(szFile
, &vlnk
))
570 MessageBox(hWnd
, g_msg_lang
[MSGID_INVALID_VLNK
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
574 for (i
= 0; i
< sizeof(vlnk
.filepath
) && vlnk
.filepath
[i
]; i
++)
576 if (vlnk
.filepath
[i
] == '.')
578 suffix
= vlnk
.filepath
+ i
;
582 if (!IsSupportedImgSuffix(suffix
))
584 MessageBox(hWnd
, g_msg_lang
[MSGID_SUFFIX_UNSUPPORTED
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
588 Utf8ToUtf16(vlnk
.filepath
, szDst
+ 2);
589 for (i
= 2; i
< MAX_PATH
&& szDst
[i
]; i
++)
598 Letter
= GetDriveLetter(vlnk
.disk_signature
, vlnk
.part_offset
);
601 MessageBox(hWnd
, g_msg_lang
[MSGID_VLNK_NO_DST
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
605 szDst
[0] = toupper(Letter
);
607 LogW(L
"vlnk dst is %ls\n", szDst
);
609 hFile
= CreateFileW(szDst
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
610 if (INVALID_HANDLE_VALUE
== hFile
)
612 MessageBox(hWnd
, g_msg_lang
[MSGID_VLNK_NO_DST
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
617 swprintf_s(Msg
, 1024, L
"%ls %ls", g_msg_lang
[MSGID_VLNK_POINT_TO
], szDst
);
618 MessageBox(hWnd
, Msg
, g_msg_lang
[MSGID_INFO
], MB_OK
| MB_ICONINFORMATION
);
623 INT_PTR CALLBACK
DialogProc(HWND hWnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
632 NotifyCode
= HIWORD(wParam
);
633 CtrlID
= LOWORD(wParam
);
635 if (NotifyCode
== BN_CLICKED
)
637 if (CtrlID
== IDC_BUTTON1
)
639 EnableWindow(g_create_button
, FALSE
);
640 CreateVlnk(hWnd
, g_CurDirW
);
641 EnableWindow(g_create_button
, TRUE
);
643 else if (CtrlID
== IDC_BUTTON2
)
645 EnableWindow(g_parse_button
, FALSE
);
647 EnableWindow(g_parse_button
, TRUE
);
654 InitDialog(hWnd
, wParam
, lParam
);
667 int APIENTRY
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, INT nCmdShow
)
672 UNREFERENCED_PARAMETER(hPrevInstance
);
674 if (GetUserDefaultUILanguage() == 0x0804)
676 g_msg_lang
= g_msg_cn
;
680 g_msg_lang
= g_msg_en
;
683 hMutex
= CreateMutexA(NULL
, TRUE
, "VtoyVlnkMUTEX");
684 if ((hMutex
!= NULL
) && (GetLastError() == ERROR_ALREADY_EXISTS
))
686 MessageBoxW(NULL
, g_msg_lang
[MSGID_RUNNING_TIP
], g_msg_lang
[MSGID_ERROR
], MB_OK
| MB_ICONERROR
);
690 GetCurrentDirectoryA(MAX_PATH
, g_CurDirA
);
691 GetCurrentDirectoryW(MAX_PATH
, g_CurDirW
);
692 sprintf_s(g_LogFile
, sizeof(g_LogFile
), "%s\\VentoyVlnk.log", g_CurDirA
);
694 for (i
= 0; i
< __argc
; i
++)
696 if (strncmp(__argv
[i
], "-Q", 2) == 0 ||
697 strncmp(__argv
[i
], "-q", 2) == 0)
705 LogA("========= VentoyVlnk =========\n");
708 DialogBoxA(hInstance
, MAKEINTRESOURCEA(IDD_DIALOG1
), NULL
, DialogProc
);