脚本:Grub4DOS Toolbox for Windows

优质
小牛编辑
131浏览
2023-12-01
引用Grub4DOS Toolbox for Windows.nsi:

#------------------------------------------------------------------------------------------#
# Project settings
SetCompressor /SOLID /FINAL lzma
Name "Grub4DOS Toolbox for Windows"
!define PROJECT "Grub4DOS Toolbox for Windows"
!define VERSION "0.1"
OutFile "${PROJECT}-${VERSION}.exe"
Caption "${PROJECT} ${VERSION}"
BrandingText "${PROJECT} by farter"
RequestExecutionLevel user

!define MULTIUSER_EXECUTIONLEVEL Standard
!define MUI_CUSTOMFUNCTION_ABORT CleanUp

!include MultiUser.nsh
!include FileFunc.nsh
!include WordFunc.nsh
!include MUI2.nsh
!include x64.nsh

!insertmacro GetDrives
!insertmacro GetSize

!define mdb "!insertmacro debugcb"
!macro debugcb Parameter
  MessageBox MB_OK "${Parameter}"
!macroend

#------------------------------------------------------------------------------------------#
# Used constants and variables

!define Sectors 49152

Var /GLOBAL WinVer
Var /GLOBAL msg
Var /GLOBAL LogFile
Var /GLOBAL TempFolder
Var /GLOBAL PriPtNumber
Var /GLOBAL ExtPtNumber
Var /GLOBAL TaskListBox
Var /GLOBAL TaskNumber
Var /GLOBAL TextBox
Var /GLOBAL TargetFile
Var /GLOBAL FileToPatch
Var /GLOBAL PatchedFile
Var /GLOBAL MenuFile
Var /GLOBAL TargetFile2
Var /GLOBAL TargetFile3
Var /GLOBAL DoButton
Var /GLOBAL FileUI1
Var /GLOBAL FilePath1
Var /GLOBAL FileBrowseButton1
Var /GLOBAL FileUI2
Var /GLOBAL FilePath2
Var /GLOBAL FileBrowseButton2
Var /GLOBAL FileUI3
Var /GLOBAL FilePath3
Var /GLOBAL FileBrowseButton3
Var /GLOBAL CheckBox1
Var /GLOBAL DropList1
Var /GLOBAL Label1
Var /GLOBAL Label2
Var /GLOBAL Label3
Var /GLOBAL Label4

#------------------------------------------------------------------------------------------#
# UI setting

#!define MUI_ICON icon.ico
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
#!define MUI_HEADERIMAGE_BITMAP "logo.bmp"

#------------------------------------------------------------------------------------------#
# win32 API defines

!define MAX_DISKS 24
!define FILE_DEVICE_MASS_STORAGE 0x2D
!define FILE_DEVICE_DISK 0x00000007
!define GENERIC_READ 0x80000000
!define GENERIC_WRITE 0x40000000
!define GENERIC_ALL 0x10000000
!define FILE_ANY_ACCESS 0
!define FILE_READ_ACCESS 1
!define FILE_SHARE_READ 1
!define FILE_SHARE_WRITE 2
!define CREATE_ALWAYS 2
!define OPEN_ALWAYS 4
!define OPEN_EXISTING 3
!define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064
!define FILE_ATTRIBUTE_NORMAL 0x80
!define NTFS_VOLUME_DATA_SIZE 96
!define METHOD_BUFFERED 0
!define METHOD_IN_DIRECT 1
!define METHOD_NEITHER 3
!define FILE_FLAG_WRITE_THROUGH 0x80000000
!define FILE_FLAG_NO_BUFFERING 0x20000000
#define CTL_CODE(DeviceType, Function, Method, Access) ( ((DeviceType) << 16) |\
                                                       ((Access) << 14) | ((Function) << 2) | (Method) )  
!define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
!define IOCTL_DISK_BASE FILE_DEVICE_DISK
#define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
!define IOCTL_STORAGE_QUERY_PROPERTY 0x002D1400
#define IOCTL_DISK_GET_DRIVE_GEOMETRY CTL_CODE(IOCTL_DISK_BASE, 0x0000, METHOD_BUFFERED, FILE_ANY_ACCESS)
!define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x00070000
#define IOCTL_DISK_GET_DRIVE_LAYOUT CTL_CODE(IOCTL_DISK_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
!define IOCTL_DISK_GET_DRIVE_LAYOUT 0x0007400C
!define PropertyStandardQuery 0
!define StorageDeviceProperty 0
!define ERROR_HANDLE_EOF 38 
#------------------------------------------------------------------------------------------#
# Pages

PageEx custom
  PageCallbacks Main
PageExEnd

#!insertmacro MUI_PAGE_INSTFILES


#------------------------------------------------------------------------------------------#
# Sections
Section Dummy DummyFlag
SectionEnd

#------------------------------------------------------------------------------------------#
# Functions

#--------------------------------------------#
# Installer init
Function .onInit

# Check user previledge
  !insertmacro MULTIUSER_INIT
  ${If} $MultiUser.Privileges != "Admin"
    MessageBox MB_YesNo|MB_ICONEXCLAMATION "你不是以管理员身份运行本软件。$\n\
                                            某些功能将不能正确运行!$\n$\n\
                                            你是否仍然继续?" IDYES +2 IDNO 0
    Quit
    Nop
  ${EndIf}

# Check multiple instances
  System::Call /NOUNLOAD  'kernel32::CreateMutexA(i 0, i 0, t "myMutex") i .r1 ?e'
  Pop $R0
  ${If} $R0 != 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "本软件的另一实例已经运行!$\n$\n\
                                         点击 '确定' 退出."
    Quit
  ${Endif}

# Check Grub4DOS files
  IfFileExists $EXEDIR\grldr +3 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "本软件必须运行于\
                                         已解包的 Grub4DOS 文件夹内!$\n$\n\
										 (即包含grldr,grldr.mbr,bootlace.com的文件夹)$\n$\n\
                                         点击 '确定' 退出。"
    Quit
  IfFileExists $EXEDIR\grldr.mbr 0 -2
  IfFileExists $EXEDIR\bootlace.com 0 -3

# Get Windows version
  ClearErrors
  ReadRegStr $WinVer HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
  
  IfErrors 0 +3
    MessageBox MB_YesNo|MB_ICONEXCLAMATION "你的 Windows 版本太低 。$\n$\n\
                                            本软件很多功能不能正确执行!$\n$\n\
                                            你是否仍然继续?" IDYES +3 IDNO 0
    Quit
  IntCmpU $WinVer 5 0 -2 0

  ${If} ${RunningX64}
    ${DisableX64FSRedirection}
  ${EndIf}
  Strcpy $TempFolder "$TEMP\G4Dtemp"
  CreateDirectory $TempFolder
  SetOutpath $TempFolder
  SectionSetFlags ${DummyFlag} 0
  StrCpy $LogFile "$EXEDIR\${PROJECT}.log"
  
FunctionEnd


#--------------------------------------------#
# Handles main dropdown list and action button
Function Main

  !insertmacro MUI_HEADER_TEXT "从  列表中选择一个任务" \
                               "点击  按钮,执行这个任务。"

# Disable and hide 'Install' button, rename 'Cancel' to 'Exit'
  GetDlgItem $0 $HWNDPARENT 1
  ShowWindow $0 ${SW_HIDE}
  EnableWindow $0 0
  GetDlgItem $0 $HWNDPARENT 2
  ${NSD_SetText} $0 "退出"

# Create dropdown task list and action button
  nsDialogs::Create /NOUNLOAD 1018
  Pop $0
  ${If} $0 == error
    Call CleanUp
    Quit
  ${EndIf}

  ${NSD_CreateLabel} 18u 2u 30u 13u "任务:"
  Pop $0

  ${NSD_CreateDroplist} 52u 0 190u 13u ""
  Pop $TaskListBox
  ${NSD_SetFocus} $TaskListBox
  ${NSD_OnChange} $TaskListBox TaskChange

  ${NSD_CreateButton} 250u 0 40u 13u ""
  Pop $DoButton
  ${NSD_SetText} $DoButton "执行"
  ${NSD_OnClick} $DoButton DoCallBack

  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:安装 Grub4DOS 到磁盘或分区 (通过 bootlace.com) "
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:恢复磁盘或分区的引导扇区 (通过已备份的镜像文件)"
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:将Grub4Dost添加到bootmgr启动项中(Vista/2008以上)"
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:修改 grldr.mbr (修改grldr.mbr内建的引导文件名)"
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:修改 grldr(修改菜单中或grldr中内建的引导文件名)"
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:显示已挂载的 Windows 分区的 UUID "
  SendMessage $TaskListBox ${CB_ADDSTRING} 0 "STR:查看版本信息"

  Call TaskChange
  nsDialogs::Show

FunctionEnd


#--------------------------------------------#
# Handels action button click
Function DoCallBack

  SendMessage $TaskListBox ${CB_GETCURSEL} 0 0 $TaskNumber
  ${If} $TaskNumber == -1
    Return
  ${EndIf}

# Display UUID and release info
  ${If} $TaskNumber > 4
    ${If} $TaskNumber == 5
      Call DisplayUUID
    ${Else}
      Call DisplayInfo
    ${EndIf}
    Return
  ${EndIf}

# Handles conditional bootmgr task first
  ${If} $TaskNumber == 2
    ${If} $WinVer >= 6
      Call Bootmgr
      Return
    ${Else}
      # Shouldn't get here, but just in case
      Call TaskChange
      Return
    ${EndIf}
  ${EndIf}

  # Check target file name in $R0
  ${If} $TaskNumber == 0
    ${NSD_GetText} $FilePath3 $R0
    StrCpy $R2 $FilePath3
  ${ElseIf} $TaskNumber > 2
    ${NSD_GetText} $FilePath1 $R0
    StrCpy $R2 $FilePath1
  ${EndIf}
  
  # Restore task has no target file
  ${If} $TaskNumber != 1
    Call CheckFileName
    ${If} $R0 == ""
      MessageBox MB_OK|MB_ICONEXCLAMATION "你必须选择一个有效的目标文件名!"
      ${NSD_SetFocus} $R2
      Return
    ${EndIf}
  
    # Enfore 8.3 format
    ${WordFind} $R0 "." "*" $R1
    ${If} $R1 > 0
      ${If} $R1 > 1
lbldo83check:
        MessageBox MB_OK|MB_ICONEXCLAMATION "目标文件必须是 8.3 文件名格式!"
        ${NSD_SetFocus} $R2
        Return
      ${Else}
        ${WordFind} $R0 "." "+02" $R1
        StrLen $R1 $R1
        ${If} $R1 > 3
          Goto lbldo83check
        ${EndIf}
      ${EndIf}
    ${Else}
      StrLen $R1 $R0
      ${If} $R1 > 8
        Goto lbldo83check
      ${EndIf}    
    ${EndIf}
    StrLen $R1 $R0
    ${If} $R1 > 12
      Goto lbldo83check
    ${EndIf}
    StrCpy $TargetFile $R0
  ${EndIf}

  ${If} $TaskNumber  4096
        MessageBox MB_YESNO|MB_ICONEXCLAMATION "$R2 大于 4096 字节,在修改时它可以被截短 $\n$\n\
                                                你是否继续?" IDYES +2 IDNO 0
        Return        
      ${EndIf}
      StrCpy $MenuFile $R2
    ${EndIf}
  ${EndIf}

  Call Patch

FunctionEnd

#--------------------------------------------#
# Handles patch tasks
Function Patch

  ${If} $TaskNumber == 3
    StrCpy $FileToPatch "$EXEDIR\grldr.mbr"
  ${Else}
    StrCpy $FileToPatch "$EXEDIR\grldr"  
  ${EndIf}
  StrCpy $R0 $PatchedFile
  StrCpy $PatchedFile $TempFolder\patched.bin
  Call PatchFileName
  Pop $0
  ${If} $0 == "error"
    Return
  ${EndIf}
  ${If} $TaskNumber != 3
  ${AndIf} $MenuFile != ""
    Call PatchMenu
  ${EndIf}
  Pop $0
  ${If} $0 == "error"
    Return
  ${EndIf}
  CopyFiles /SILENT $PatchedFile $R0
  MessageBox MB_OK "$R0 已被保存."
  Return

FunctionEnd

#--------------------------------------------#
# Patch menu.lst of $PatchedFile with $MenuFile
Function PatchMenu
  System::Store "S"
  IntOp $R0 512 << 6
  
  # Read source file
  StrCpy $R1 $PatchedFile
  System::Call "kernel32::CreateFile(t R1, i ${GENERIC_ALL}, \
                                     i ${FILE_SHARE_READ} | ${FILE_SHARE_WRITE}, i 0, \
                                     i ${OPEN_EXISTING}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R1"
  ${If} $R1  $R2
    MessageBox MB_OK|MB_ICONEXCLAMATION "$FileToPatch 文件不是一个有效的Grub4DOS文件"
lblpatchmenu1:
    System::Call "kernel32::CloseHandle(i R1)"
    Goto lblpatchmenu 
  ${EndIf}
  System::Alloc 512
  Pop $R3
  
  ${For} $R9 0 63
    System::Call "kernel32::ReadFile(i R1, i R3 , i 512, *i .R4, i ..)"
    ${If} $R4 != 512
      MessageBox MB_OK|MB_ICONEXCLAMATION "临时文件读取错误"
lblpatchmenu2:
      System::Free $R3
      Goto lblpatchmenu1 
    ${EndIf}

    System::Call "*$R3(i .R4)"
    IntFmt $R4 "0x%08X" $R4
    ${If} $R4 == 0x008270EA
      ${ExitFor}
    ${EndIf}
  ${Next}
  ${If} $R4 != 0x008270EA
lblpatchmenu3:
    MessageBox MB_OK|MB_ICONEXCLAMATION "$FileToPatch 文件不是一个有效的Grub4DOS文件"
    Goto lblpatchmenu2
  ${EndIf}

  System::Call "*$R3(&i108 .., i .R4)"
  IntOp $R4 $R4 - 0x83F0
  ${If} $R4 < 0
    Goto lblpatchmenu3
  ${EndIf}
  System::Free $R3
  System::Call "kernel32::SetFilePointer(i R1, i R4, i 0, i 1) i .R5"
  ${If} $R5 < 0
    Goto lblpatchmenu3
  ${EndIf}
  FileOpen $0 $MenuFile r
  ${If} $0 == ""
    MessageBox MB_OK|MB_ICONEXCLAMATION "$MenuFile 打开错误"
    Goto lblpatchmenu4
  ${EndIf}
lblpatchmenuloop:
  ClearErrors
  FileRead $0 $1
  IfErrors lblpatchmenuloopend
  StrCpy $2 $1 1
  ${If} $2 == "#"
  ${OrIf} $2 == "$\n"
  ${OrIf} $2 == "$\r"
    Goto lblpatchmenuloop
  ${EndIf}
  FileWrite $R1 $1
  Goto lblpatchmenuloop
lblpatchmenuloopend:
  FileClose $0
lblpatchmenu4:
  System::Call "kernel32::SetEndOfFile(i R1)"
  System::Call "kernel32::CloseHandle(i R1)"
  System::Store "L"
  Push "success"
  Return
FunctionEnd


#--------------------------------------------#
# Setup appropriate UI for selected task
Function TaskChange
# Clear UI
  SendMessage $TextBox ${WM_CLOSE} 0 0
  SendMessage $FileUI1 ${WM_CLOSE} 0 0
  SendMessage $FilePath1 ${WM_CLOSE} 0 0
  SendMessage $FileBrowseButton1 ${WM_CLOSE} 0 0
  SendMessage $FileUI2 ${WM_CLOSE} 0 0
  SendMessage $FilePath2 ${WM_CLOSE} 0 0
  SendMessage $FileBrowseButton2 ${WM_CLOSE} 0 0
  SendMessage $FileUI3 ${WM_CLOSE} 0 0
  SendMessage $FilePath3 ${WM_CLOSE} 0 0
  SendMessage $FileBrowseButton3 ${WM_CLOSE} 0 0
  SendMessage $CheckBox1 ${WM_CLOSE} 0 0
  SendMessage $DropList1 ${WM_CLOSE} 0 0
  SendMessage $Label1 ${WM_CLOSE} 0 0
  SendMessage $Label2 ${WM_CLOSE} 0 0
  SendMessage $Label3 ${WM_CLOSE} 0 0
  SendMessage $Label4 ${WM_CLOSE} 0 0
  
  SendMessage $TaskListBox ${CB_GETCURSEL} 0 0 $TaskNumber
  ${If} $TaskNumber == -1
    EnableWindow $DoButton 0
    Return
  ${EndIf}

# Handles conditional bootmgr task first
  ${If} $TaskNumber == 2
  ${AndIf} $WinVer  4
    nsDialogs::CreateControl /NOUNLOAD ${__NSD_Text_CLASS} \
                                       ${DEFAULT_STYLES}|${WS_TABSTOP}|${ES_MULTILINE}|${ES_READONLY}|${WS_VSCROLL} \
                                       ${__NSD_Text_EXSTYLE} 0 43 100% 70% ""
    Pop $TextBox
    Call DoCallBack
    Return
  ${EndIf}

# Bootmgr task
  ${If} $TaskNumber == 2
    ${NSD_CreateGroupBox} 7u 20u -13u 35u "选择将使用的 bootmgr 文件 (留空将使用 Windows 默认的):"
    Pop $FileUI1
    ${NSD_CreateDropList} 20u 34u -95u 13u ""
    Pop $FilePath1
    ${NSD_CreateBrowseButton}  -70u 34u 50u 13u "刷新"
    Pop $FileBrowseButton1
    ${NSD_OnClick} $FileBrowseButton1 GetBootmgr
    ${NSD_CreateGroupBox} 7u 60u -13u 35u "输入将使用的 Grub4DOS 的 mbr 文件名:"
    Pop $FileUI2
    ${NSD_CreateDirRequest} 20u 74u -95u 13u ""
    Pop $FilePath2
    ${NSD_SetText} $FilePath2 "grldr.mbr"

    ${NSD_CreateGroupBox} 7u 100u -13u 35u "为在 bootmgr 中的 GRUB4DOS 启动项指定选项:"
    Pop $FileUI3
    ${NSD_CreateLabel} 20u 116u 20u 13u "Title:"
    Pop $Label1
    ${NSD_CreateDirRequest} 40u 114u 70u 13u "Grub4DOS"
    Pop $FilePath3
    ${NSD_CreateCheckBox} 125u 114u 65u 13u "Boot by default"
    Pop $CheckBox1
    ${NSD_CreateLabel} 195u 116u 65u 13u "Timeout (seconds)"
    Pop $Label2
    ${NSD_CreateDroplist} 262u 114u 25u 13u ""
    Pop $DropList1
    SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:5"
    SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:15"
    SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:30"
    Call GetBootmgr
    Return
  ${EndIf}

# bootlace.com and restore tasks
  ${If} $TaskNumber < 2
    ${If} $TaskNumber < 1
      ${NSD_CreateGroupBox} 7u 20u -13u 50u "选择 GRUB4DOS 安装到的目标设备:"
    ${Else}
      ${NSD_CreateGroupBox} 7u 20u -13u 50u "选择恢复到的目标设备:"
    ${EndIf}
    Pop $FileUI1
    ${NSD_CreateLabel} 20u 34u 30u 13u "磁盘: "
    Pop $Label1

    ${NSD_CreateDroplist} 55u 33u 180u 13u ""
    Pop $FilePath1
    ${NSD_OnChange} $FilePath1 EnumParts

    ${NSD_CreateButton} 245u 33u 40u 13u "刷新"
    Pop $FileBrowseButton1
    ${NSD_OnClick} $FileBrowseButton1 EnumDisks
  
    ${NSD_CreateLabel} 20u 50u 30u 13u "分区: "
    Pop $Label2

    ${NSD_CreateDroplist} 55u 49u 180u 13u ""
    Pop $FilePath2
    ${NSD_OnChange} $FilePath2 AutoSetOptions
    
    ${NSD_CreateButton} 245u 49u 40u 13u "刷新"
    Pop $FileBrowseButton2
    ${NSD_OnClick} $FileBrowseButton2 EnumParts

    ${If} $TaskNumber < 1
      ${NSD_CreateGroupBox} 7u 75u -13u 65u "设置bootlace.com安装选项:"
      Pop $FileUI2
      ${NSD_CreateLabel} 15u 90u 40u 13u "引导文件:"
      Pop $Label3
      ${NSD_CreateDirRequest} 55u 89u 50u 13u ""
      Pop $FilePath3
      ${NSD_SetText} $FilePath3 "grldr"

      ${NSD_CreateLabel} 110u 90u 30u 13u "选项:"
      Pop $Label4
      ${NSD_CreateDroplist} 142u 89u 145u 13u ""
      Pop $DropList1
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--read-only"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--mbr-no-bpb"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--no-backup-mbr"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--force-backup-mbr"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--mbr-enable-floppy"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--mbr-disable-floppy"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--mbr-enable-osbr"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--mbr-disable-osbr"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--duce"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--chs-no-tune"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--boot-prevmbr-first"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--boot-prevmbr-last"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--preferred-drive=0"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--preferred-partition=0"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--serial-number=1"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--time-out=5"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--hot-key=0x3920"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--floppy"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--floppy=0"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--sectors-per-track=63"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--heads=255"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--start-sector=1"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--total-sectors=0"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--lba"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--chs"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--fat12"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--fat16"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--fat32"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--vfat"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--ntfs"
      SendMessage $DropList1 ${CB_ADDSTRING} 0 "STR:--ext2"
      ${NSD_OnChange} $DropList1 SetOptions

      nsDialogs::CreateControl /NOUNLOAD ${__NSD_Text_CLASS} \
                                         ${DEFAULT_STYLES}|${WS_TABSTOP}|${ES_MULTILINE}|${WS_VSCROLL} \
                                         ${__NSD_Text_EXSTYLE} 20 75% -35 20% ""
      Pop $TextBox

    # Restore task
    ${Else}
      ${NSD_CreateGroupBox} 7u 100u -13u 35u "选择已备份的镜像文件:"
      Pop $FileUI3
      ${NSD_CreateDirRequest} 20u 114u -95u 13u ""
      Pop $FilePath3
      ${NSD_CreateBrowseButton}  -70u 114u 50u 13u "浏览"
      Pop $FileBrowseButton3
      StrCpy $TargetFile3 "*.bin"
      ${NSD_OnClick} $FileBrowseButton3 BrowseFile3    
    ${EndIf}
    
    Call EnumDisks
    Return
  ${EndIf}

# Patch file tasks    
  ${NSD_CreateGroupBox} 7u 20u -13u 35u "指定目标文件名:"
  Pop $FileUI1
  ${NSD_CreateDirRequest} 20u 34u -95u 13u ""
  Pop $FilePath1
  ${NSD_SetText}  $FilePath1 "mygrldr"

  ${If} $TaskNumber == 4
    ${NSD_CreateGroupBox} 7u 60u -13u 35u "选择被使用的 menu.lst 文件:"
    Pop $FileUI2
    ${NSD_CreateDirRequest} 20u 74u -95u 13u ""
    Pop $FilePath2
    ${NSD_SetText} $FilePath2 $EXEDIR\menu.lst
    ${NSD_CreateBrowseButton}  -70u 74u 50u 13u "浏览"
    Pop $FileBrowseButton2
    StrCpy $TargetFile2 "*.lst"
    ${NSD_OnClick} $FileBrowseButton2 BrowseFile2
  ${EndIf}

  ${NSD_CreateGroupBox} 7u 100u -13u 35u "指定输出文件:"
  Pop $FileUI3
  ${NSD_CreateDirRequest} 20u 114u -95u 13u ""
  Pop $FilePath3
  ${NSD_CreateBrowseButton}  -70u 114u 50u 13u "浏览"
  Pop $FileBrowseButton3
  ${If} $TaskNumber == 4
    ${NSD_SetText} $FilePath3 "$EXEDIR\mygrldr"  
  ${EndIf}
  StrCpy $TargetFile3 "*.*"
  ${NSD_OnClick} $FileBrowseButton3 BrowseFile3

FunctionEnd


#--------------------------------------------#
# Install using bootlace.com
Function Install

  # Get file handle $R4
  StrCpy $R4 0
  System::Call "kernel32::CreateFile(t R3, i ${GENERIC_READ}, i ${FILE_SHARE_WRITE}, \
                                     i 0, i ${OPEN_EXISTING}, i 0, i 0) i .R4"
  ${If} $R4 = 0
    System::Alloc 4096
    Pop $1
    System::Call "kernel32::DeviceIoControl(i R4, i ${IOCTL_DISK_GET_DRIVE_LAYOUT}, i 0, \
                                            i 0, i r1, i 4096, *i .R5, i 0) i .r2"
    ${If} $2 == 0
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
      Goto lblinstallret
    ${EndIf}

    System::Call "*$1(i .r2)"
    ${If} $2 < 1
      MessageBox MB_OK|MB_ICONEXCLAMATION "获取分区信息错误"
      System::Free $1
lblinstallret:
      System::Call "kernel32::CloseHandle(i R4)"
      ${NSD_SetFocus} $FilePath1
      Call EnumDisks
      Return
    ${EndIf}

    StrCpy $PriPtNumber 0
    StrCpy $ExtPtNumber 4
    IntOp $0 $1 + 8
    ${For} $3 1 $2
      System::Call "*$0(l .R5)"
      ${If} $R5 != 0
        System::Call "*$0(&i20 .., i .r7)"
        ${If} $7 == 0
          ${If} $3 <= 4
            # Extended partition
            IntOp $PriPtNumber $PriPtNumber + 1
          ${EndIf}
          GoTo lblinstallloop
        ${EndIf}
        ${If} $3 = 0
    System::Call "kernel32::SetFilePointerEx(i R4, l R5, *l .R6, 0)"
    System::Int64Op $R5 = $R6
    Pop $R6
    ${If} $R6 != 1
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
      Goto lblinstallret
    ${EndIf}
  ${EndIf}

  # Read sectors
  System::Alloc ${Sectors}
  Pop $R6
  
  System::Call "kernel32::ReadFile(i R4, i R6 , i ${Sectors}, *i .R7, i ..)"
  ${If} $R7 != ${Sectors}
    MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
lblinstallret1:
    System::Free $R6
    GoTo lblinstallret
  ${EndIf}
  System::Call "kernel32::CloseHandle(i R4)"
  
  # Save sector file
  StrCpy $R4 $TempFolder\image.bin
  System::Call "kernel32::CreateFile(t R4, i ${GENERIC_READ}|${GENERIC_WRITE}, i 0, i 0, \
                                     i ${CREATE_ALWAYS}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R4"
  ${If} $R4 <= 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "建立镜像文件错误"
    GoTo lblinstallret1
  ${EndIf}
  System::Call "kernel32::WriteFile(i R4, i R6 , i ${Sectors}, *i .R7, i ..)"  
  ${If} $R7 != ${Sectors}
    MessageBox MB_OK|MB_ICONEXCLAMATION "写入镜像文件错误"
    GoTo lblinstallret1
  ${EndIf}
  System::Call "kernel32::CloseHandle(i R4)"
  CopyFiles /SILENT $TempFolder\image.bin $TempFolder\image.bak
  
  # Patch and call bootlace.com
  StrCpy $FileToPatch $EXEDIR\bootlace.com
  StrCpy $PatchedFile $TempFolder\bootlace.com
  Call PatchFileName
  Pop $0
  ${If} $0 == "error"
    Return
  ${EndIf}
  
  # Get bootlace.com options $R8
  ${NSD_GetText} $TextBox $R8
  ${WordReplace} $R8 "$\n" "" "+" $R8
  ${WordReplace} $R8 "$\r" "" "+" $R8
  ${WordReplace} $R8 "$\t" " " "+" $R8
  
  nsExec::ExecToStack '"bootlace.com" $R8 image.bin'
  Pop $0
  Pop $1
  ${WordReplace} $1 "Invalid keyboard code specified$\r" " " "+" $1
  ${If} $0 != 0
    ${WordFind3X} $1 "$\r" "Error" "$\r" "+1" $1
    StrCpy $1 "Bootlace.com 返回以下错误信息:$\r$\n\
               --------------------------------------------------------------$\r$\r\
               $1$\r$\r\
               --------------------------------------------------------------"
    MessageBox MB_OK "$1"
    System::Free $R6
    Return
  ${EndIf}

  # Wirte sectors if not 'read-only'
  StrCpy $1 "Bootlace.com 返回以下成功信息:$\r\
             --------------------------------------------------------------$\r$1\
             --------------------------------------------------------------"
  ${WordReplace} $R8 "--read-only" "" "+" $R9
  ${If} $R8 != $R9
    MessageBox MB_OK "$1"
    System::Free $R6
    Return
  ${EndIf}
  
  StrCpy $1 "$1$\r是否正式写入目标设备"
  MessageBox MB_YESNO "$1" IDYES +3 IDNO 0
    System::Free $R6
    Return

  # Read sector file
  StrCpy $R4 $TempFolder\image.bin
  System::Call "kernel32::CreateFile(t R4, i ${GENERIC_READ}|${GENERIC_WRITE}, i 0, i 0, \
                                     i ${OPEN_EXISTING}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R4"
  ${If} $R4 <= 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "Error opening image file!"
    GoTo lblinstallret1
  ${EndIf}
  System::Call "kernel32::ReadFile(i R4, i R6 , i ${Sectors}, *i .R7, i ..)"

  ${If} $R7 != ${Sectors}
    MessageBox MB_OK|MB_ICONEXCLAMATION "镜像文件读取错误"
    GoTo lblinstallret1
  ${EndIf}  
  System::Call "kernel32::CloseHandle(i R4)"
  
  # Get file handle $R4
  StrCpy $R4 0
  System::Call "kernel32::CreateFile(t R3, i ${GENERIC_WRITE}, i ${FILE_SHARE_WRITE}, \
                                     i 0, i ${OPEN_EXISTING}, \
                                     i ${FILE_FLAG_NO_BUFFERING} | ${FILE_FLAG_WRITE_THROUGH}, i 0) i .R4"
  ${If} $R4 = 0
    System::Call "kernel32::SetFilePointerEx(i R4, l R5, *l .R7, i 0)"
    System::Int64Op $R5 = $R7
    Pop $R7
    ${If} $R7 != 1
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
      Goto lblinstallret1
    ${EndIf}
  ${EndIf}

  # Write sectors
  System::Call "kernel32::WriteFile(i R4, i R6 , i ${Sectors}, *i .R7, i ..)"
  ${If} $R7 != ${Sectors}
    MessageBox MB_OK|MB_ICONEXCLAMATION "设备写入错误"
    GoTo lblinstallret1
  ${EndIf}
  System::Call "kernel32::CloseHandle(i R4)"
  System::Free $R6
  
  # Save backup
  ${GetTime} "" L $0 $1 $2 $3 $4 $5 $6
  StrCpy $R5 "$EXEDIR\$2-$1-$0-$4-$5-$6.bin"
  CopyFiles /SILENT $TempFolder\image.bak $R5
  
  # Write log
  ${NSD_GetText} $FilePath1 $R1
  ${NSD_GetText} $FilePath2 $R2
  ${NSD_GetText} $FilePath3 $R3
  ${NSD_GetText} $TextBox $R4
  StrCpy $R0 "Grub4DOS 已安装到设备.$\r$\n\
              $\r$\n\
              目标设备: $R1$\r$\n\
              目标分区: $R2$\r$\n\
              目标文件: $R3$\r$\n\
              Bootlace.com 选项: $R4$\r$\n\
              备份文件: $R5$\r$\n\
              $\r$\n"
  Call WriteLog
  
  MessageBox MB_OK "Grub4DOS 已成功安装!"

FunctionEnd

#--------------------------------------------#
# Set bootlace.com commandline options
Function SetOptions
  ${NSD_GetText} $DropList1 $R1
  ${If} $R1 == ""
    Return
  ${EndIf}
  
  ${NSD_GetText} $TextBox $R0
  ${WordReplace} $R0 "$\n" "" "+" $R0
  ${WordReplace} $R0 "$\r" "" "+" $R0
  ${WordReplace} $R0 "$\t" " " "+" $R0
  StrCpy $R0 "$R0$R1 "
  ${NSD_SetText} $TextBox $R0
FunctionEnd


#--------------------------------------------#
# Set bootlace.com commandline options according to selected disk/partition
Function AutoSetOptions
  StrCpy $R0 ""
  ${NSD_GetText} $FilePath1 $R1
  ${NSD_GetText} $FilePath2 $R2
  SendMessage $FilePath2 ${CB_GETCOUNT} 0 0 $R3
  
  ${If} $R1 != ""
    ${If} $R2 != ""
      # Get partition number
      ${WordFind} $R2 " " "+1" $R2
      StrCpy $R0 "--floppy=$R2 "
    ${ElseIf} $R3 < 1
      StrCpy $R0 "--floppy "      
    ${EndIf}
  ${EndIf}
  
  ${NSD_SetText} $TextBox $R0
FunctionEnd

#--------------------------------------------#
# Restore using bootlace.com and backup image
Function Restore

  # Image file name in $R0
  ${NSD_GetText} $FilePath3 $R0
  IfFileExists $R0 0 +3
  IfFileExists $R0\*.* +2 +4 
  StrCmp $R0 "" 0 +3
    MessageBox MB_OK|MB_ICONEXCLAMATION "你必须指定一个有效的镜像文件"
    Return

  # Get image file handle in $R0
  ${NSD_GetText} $FilePath3 $R0
  System::Call "kernel32::CreateFile(t R0, i ${GENERIC_READ}, i 0, i 0, \
                                     i ${OPEN_EXISTING}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R0"  
  ${If} $R0 <= 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "镜像文件打开错误"
    Return
  ${EndIf}
  
  # Get imgae file size in $R2
  IntOp $R4 $R2 + 0
  System::Alloc 8
  POp $2
  IntOp $R9 0 + 0
  System::Call "kernel32::GetFileSizeEx(i R0, i r2) i .R9"
  ${If} $R9 == 0
    MessageBox MB_OK|MB_ICONEXCLAMATION "镜像文件读取错误"
    System::Free $2
    Goto lblrestore
  ${EndIf}
  System::Call "*$2(l .R2)"
  System::Free $2
  System::Int64Op $R2 = ${Sectors}
  Pop $R9
  ${If} $R9 != 1
    ${NSD_GetText} $FilePath3 $R9
    MessageBox MB_YESNO|MB_ICONEXCLAMATION "$R9 不是本程序建立的备份镜像 $\n$\n\
                                            是否继续?" IDYES 0 IDNO lblrestore
  ${EndIf}

  # Get target file handle in $R1
  # partition number is in $R4, device file name is in $R3
  System::Call "kernel32::CreateFile(t R3, i ${GENERIC_READ}, i 0, \
                                     i 0, i ${OPEN_EXISTING}, i 0, i 0) i .R1"
  ${If} $R1 = 0
    System::Alloc 4096
    Pop $1
    System::Call "kernel32::DeviceIoControl(i R1, i ${IOCTL_DISK_GET_DRIVE_LAYOUT}, i 0, \
                                            i 0, i r1, i 4096, *i .R5, i 0) i .r2"
    ${If} $2 == 0
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
      Goto lblrestore
    ${EndIf}

    System::Call "*$1(i .r2)"
    ${If} $2 < 1
      MessageBox MB_OK|MB_ICONEXCLAMATION "获取分区信息错误"
      System::Free $1
      ${NSD_SetFocus} $FilePath1
      Call EnumDisks
      Goto lblrestore1
    ${EndIf}

    StrCpy $PriPtNumber 0
    StrCpy $ExtPtNumber 4
    IntOp $0 $1 + 8
    ${For} $3 1 $2
      System::Call "*$0(l .R5, l .R6)"
      ${If} $R5 != 0
        System::Call "*$0(&i20 .., i .r7)"
        ${If} $7 == 0
          ${If} $3 <= 4
            # Extended partition
            IntOp $PriPtNumber $PriPtNumber + 1
          ${EndIf}
          GoTo lblrestoreloop
        ${EndIf}
        ${If} $3 <= 4
          StrCpy $7 $PriPtNumber
          IntOp $PriPtNumber $PriPtNumber + 1
        ${Else}
          StrCpy $7 $ExtPtNumber
          IntOp $ExtPtNumber $ExtPtNumber + 1
        ${EndIf}
        ${If} $7 == $R4
          ${ExitFor}
        ${EndIf}  
      ${EndIf}

lblrestoreloop:
      IntOp $0 $0 + 32
    ${Next}
    System::Free $1
  ${EndIf}

  # Set file pointer
  System::Call "kernel32::CloseHandle(i R1)"
  System::Call "kernel32::CreateFile(t R3, i ${GENERIC_WRITE}, i 0, \
                                     i 0, i ${OPEN_EXISTING}, \
                                     i ${FILE_FLAG_NO_BUFFERING} | ${FILE_FLAG_WRITE_THROUGH}, i 0) i .R1"
  ${If} $R1 = 0
    System::Call "kernel32::SetFilePointerEx(i R1, l R5, *l .R9, i 0)"
    System::Int64Op $R5 = $R9
    Pop $R9
    ${If} $R9 != 1
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备读取错误"
      Goto lblrestore1
    ${EndIf}
  ${EndIf}
  
  # Get buffer in $R7
  System::Alloc ${Sectors}
  Pop $R7
  
  # Read/Write loop
  # Determine $0 bytes to copy
  IntOp $0 ${Sectors} + 0
  # if image size $R2 is not SECTORS, use image size
  System::Int64Op $0 = $R2
  Pop $1
  ${If} $1 != 1
    IntOp $0 $R2 + 0
    # if target is partition and size $R6 is smaller than image size, use target size
    ${If} $R4 >= 0
      System::Int64Op $0 > $R6
      Pop $1
      ${If} $1 == 1
        System::Int64Op $R6 + 0
        Pop $0
      ${EndIf}    
    ${EndIf}
  Pop $1  
  ${EndIf}
  
  # actual loop. stores bytes to read per loop in $1
  IntOp $1 ${Sectors} + 0
  
lblrestorecopyloop:
  System::Int64Op $1 > $0
  Pop $2
  ${If} $2 == 1
    System::Int64Op $0 + 0
    Pop $1  
  ${EndIf}
  
  System::Call "kernel32::ReadFile(i R0, i R7 , i r1, *i .R8, i ..)"
  ${If} $R8 != $1
    MessageBox MB_OK|MB_ICONEXCLAMATION "镜像文件读取错误"
    Goto lblrestore2    
  ${EndIf}

  System::Call "kernel32::WriteFile(i R1, i R7 , i r1, *i .R8, i ..)"
  ${If} $R8 != $1
    System::Call "kernel32::GetLastError() i .R9"
    ${If} $R9 != ${ERROR_HANDLE_EOF}
      MessageBox MB_OK|MB_ICONEXCLAMATION "设备写入错误 $R9"
    ${EndIf}
    Goto lblrestore2    
  ${EndIf}
  
  System::Int64Op $0 - $1
  Pop $0
  ${If} $0 > 0
    Goto lblrestorecopyloop
  ${EndIf}
  
lblrestore2:    
  System::Free $R7
lblrestore1:
  System::Call "kernel32::CloseHandle(i R1)"  
lblrestore:
  System::Call "kernel32::CloseHandle(i R0)"

  # Write log
  ${NSD_GetText} $FilePath1 $R1
  ${NSD_GetText} $FilePath2 $R2
  ${NSD_GetText} $FilePath3 $R3
  ${NSD_GetText} $TextBox $R4
  StrCpy $R0 "Restored image file to device.$\r$\n\
              $\r$\n\
              Source file: $R3$\r$\n\
              Target disk: $R1$\r$\n\
              Target partition: $R2$\r$\n\
              $\r$\n"
  Call WriteLog
  MessageBox MB_OK "镜像文件成功写入设备"
  
FunctionEnd

#--------------------------------------------#
# Locate drives with bootmgr in the root folder
Function GetBootmgr
  SendMessage $FilePath1 ${CB_RESETCONTENT} 0 0 $0
  ${GetDrives} "FDD+HDD+NET" "CheckBootmgr"
FunctionEnd

Function CheckBootmgr
  IfFileExists $9bootmgr 0 lblgetbootmgr
  # $9 is driveletter; Stores partition type in $7
  System::Call /NOUNLOAD 'kernel32::GetVolumeInformation(t r9, t .r8, i 1024, \
                                                         *i .r1, i .., i .., t .r7, i 1024 ) i ..'
  ${If} $7 == "NTFS"
    SendMessage $FilePath1 ${CB_ADDSTRING} 0 "STR:$9bootmgr"
  ${EndIf}
lblgetbootmgr:
  Push "Keep Searching"
FunctionEnd


#--------------------------------------------#
# Opens file selection dialog
Function BrowseFile1
  nsDialogs::SelectFileDialog /NOUNLOAD open "" "$TargetFile1|$TargetFile1"
  Pop $R0
  ${NSD_SetText} $FilePath1 $R0
FunctionEnd

Function BrowseFile2
  nsDialogs::SelectFileDialog /NOUNLOAD open "" "$TargetFile2|$TargetFile2"
  Pop $R0
  ${NSD_SetText} $FilePath2 $R0
FunctionEnd

Function BrowseFile3
  nsDialogs::SelectFileDialog /NOUNLOAD open "" "$TargetFile3|$TargetFile3"
  Pop $R0
  ${NSD_SetText} $FilePath3 $R0
FunctionEnd


#--------------------------------------------#
# Enumerate connected disks
Function EnumDisks

  SendMessage $FilePath1 ${CB_RESETCONTENT} 0 0 $0
  EnableWindow $FilePath1 1

  SendMessage $FilePath2 ${CB_RESETCONTENT} 0 0 $0
  EnableWindow $FilePath2 1

  StrCpy $R0 "\\.\A:"
  Call CheckDisk
  ${If} $R1 != ""
    SendMessage $FilePath1 ${CB_ADDSTRING} 0 "STR:fd0:$R1"
  ${EndIf}
  StrCpy $R0 "\\.\B:"
  Call CheckDisk
  ${If} $R1 != ""
    SendMessage $FilePath1 ${CB_ADDSTRING} 0 "STR:fd1:$R1"
  ${EndIf}
        
  ${For} $1 0 ${MAX_DISKS}
    StrCpy $R0 "\\.\PhysicalDrive$1"
    Call CheckDisk
    ${If} $R1 != ""
      SendMessage $FilePath1 ${CB_ADDSTRING} 0 "STR:hd$1:$R1"
    ${EndIf}
  ${Next}
  
  Call AutoSetOptions
FunctionEnd

#--------------------------------------------#
# Check whether $R0 exists and stores disk info in $R1
Function CheckDisk

  StrCpy $R1 ""
  System::Call "kernel32::CreateFile(t R0, i 0, i ${FILE_SHARE_READ}|${FILE_SHARE_WRITE}, \
                                     i 0, i ${OPEN_EXISTING}, i 0, i 0) i .R2"
  ${If} $R2 > 0
    System::Alloc 24
    Pop $R3
    System::Call "kernel32::DeviceIoControl(i R2, i ${IOCTL_DISK_GET_DRIVE_GEOMETRY}, i 0, \
                                            i 0, i R3, i 24, *i .R5, i 0) i .R4"
    ${If} $R4 == 0
      Return
    ${EndIf}
    System::Call "*$R3(&i8 .R4, i .R5, i .R6, i .R7, i .R8)"
    System::Free $R3
    IntOp $R6 $R6 * $R7
    IntOp $R6 $R6 * $R8
    System::Int64Op $R4 * $R6
    Pop $R4
    Call GetDiskSize
    StrCpy $R8 $R4
    
    System::Call "*(i ${StorageDeviceProperty}, i ${PropertyStandardQuery}, i 0) i .s"
    Pop $R3
    System::Alloc 1024
    Pop $R4
    System::Call "kernel32::DeviceIoControl(i R2, i ${IOCTL_STORAGE_QUERY_PROPERTY}, \
                                            i R3, i 12, i R4, i 1024, *i .R5, i 0) i .."
    ${If} $R5 > 0
  
# Get BusType
      IntOp $R5 $R4 + 28
      System::Call "*$R5(i .R6)"
      ${Select} $R6
        ${Case} "1"
          StrCpy $R6 "SCSI"
        ${Case} "3"
          StrCpy $R6 "ATA"
        ${Case} "4"
          StrCpy $R6 "1394"
        ${Case} "5"
          StrCpy $R6 "SSA"
        ${Case} "6"
          StrCpy $R6 "FIBRE"
        ${Case} "7"
          StrCpy $R6 "USB"
        ${Case} "8"
          StrCpy $R6 "RAID"
        ${Case} "9"
          StrCpy $R6 "iSCSI"
        ${Case} "10"
          StrCpy $R6 "SAS"
        ${Case} "11"
          StrCpy $R6 "SATA"
        ${Case} "12"
          StrCpy $R6 "SD"
        ${Case} "13"
          StrCpy $R6 "MMC"
        ${CaseElse}
          StrCpy $R6 ""
      ${EndSelect}
      ${If} $R6 != ""
        StrCpy $R1 "$R6:"
      ${EndIf}

# Get VendorID
      IntOp $R5 $R4 + 12
      System::Call "*$R5(i .R6)"
      ${If} $R6 != 0
        IntOp $R5 $R4 + $R6
        System::Call "*$R5(&t1024 .R6)"
        ${WordReplace} $R6 " " "" "{}*" $R7
        ${If} $R7 != ""
          Strcpy $R1 $R1$R7-
        ${EndIf}
      ${EndIf}
      
# Get ProductID
      IntOp $R5 $R4 + 16
      System::Call "*$R5(i .R6)"
      ${If} $R6 != 0
        IntOp $R5 $R4 + $R6
        System::Call "*$R5(&t1024 .R6)"
        ${WordReplace} $R6 " " "" "{}*" $R7
        ${If} $R1 != ""
        ${AndIf} $R7 != ""
          Strcpy $R1 "$R1$R7"
        ${EndIf}
      ${EndIf}
      
# Get ProductRevision
      IntOp $R5 $R4 + 20
      System::Call "*$R5(i .R6)"
      ${If} $R6 != 0
        IntOp $R5 $R4 + $R6
        System::Call "*$R5(&t1024 .R6)"
        ${WordReplace} $R6 " " "" "{}*" $R7
        ${If} $R7 != ""
          Strcpy $R1 "$R1($R7)"
        ${EndIf}
      ${EndIf}
    ${EndIf}
    
    StrCpy $R1 "$R1 ($R8)"
    System::Free $R3
    System::Free $R4
    System::Call "kernel32::CloseHandle(i R2)"
  ${EndIf}
  
FunctionEnd

#--------------------------------------------#
# Enumerate partitions of selected disk
Function EnumParts

  SendMessage $FilePath2 ${CB_RESETCONTENT} 0 0 $R1
  ${NSD_GetText} $FilePath1 $R1
  StrCpy $R2 $R1 1 4
  ${If} $R2 != ":"
    StrCpy $R2 $R1 3
  ${Else}
    StrCpy $R2 $R1 4  
  ${EndIf}
  StrCpy $PriPtNumber 0
  StrCpy $ExtPtNumber 4
  
  ${If} $R2 == "fd0"
  ${OrIf} $R2 == "fd1"
    Return
  ${EndIf}
  
  StrCpy $R2 $R2 "" 2
  StrCpy $R0 "\\.\PhysicalDrive$R2"
  System::Call "kernel32::CreateFile(t R0, i ${GENERIC_READ}, i ${FILE_SHARE_WRITE}, i 0, \
                                     i ${OPEN_EXISTING}, i 0, i 0) i .R2"
  ${If} $R2 > 0
    System::Alloc 4096
    Pop $R1
    System::Call "kernel32::DeviceIoControl(i R2, i ${IOCTL_DISK_GET_DRIVE_LAYOUT}, i 0, \
                                            i 0, i R1, i 4096, *i .R5, i 0) i .R4"
    ${If} $R4 == 0
      Goto lblenumptret
    ${EndIf}

# typedef struct _DRIVE_LAYOUT_INFORMATION {
# DWORD PartitionCount;
# DWORD Signature;
# PARTITION_INFORMATION PartitionEntry[1];
# } DRIVE_LAYOUT_INFORMATION, *PDRIVE_LAYOUT_INFORMATION;

# Get number of partitions
    System::Call "*$R1(i .R5)"
    ${If} $R5 < 1
      Goto lblenumptret
    ${EndIf}
    
    IntOp $R0 $R1 + 8

# typedef struct _PARTITION_INFORMATION {
# LARGE_INTEGER StartingOffset;
# LARGE_INTEGER PartitionLength;
# DWORD         HiddenSectors;
# DWORD         PartitionNumber;
# BYTE          PartitionType;
# BOOLEAN       BootIndicator;
# BOOLEAN       RecognizedPartition;
# BOOLEAN       RewritePartition;
# } PARTITION_INFORMATION,  *PPARTITION_INFORMATION;

    ${For} $R3 1 $R5
      System::Call "*$R0(l .R6)"
      ${If} $R6 != 0
        System::Call "*$R0(l .., l .R4)"
        Call GetDiskSize
        System::Call "*$R0(&i20 .., i .R7, &i1 .R8, &i1 .R9 )"
        ${If} $R7 == 0
          ${If} $R3 <= 4
            # Extended partition
            IntOp $PriPtNumber $PriPtNumber + 1
          ${EndIf}
          GoTo lblenumptloop
        ${EndIf}
        ${If} $R3  5000
    System::Int64Op $R4 / 1000
    Pop $R4
    ${If} $R4 > 5000
      IntOp $R4 $R4 / 1000
      ${If} $R4 > 5000
        IntOp $R4 $R4 / 1000
        ${If} $R4 > 5000
          IntOp $R4 $R4 / 1000
          StrCpy $R4 "$R4T"
        ${EndIf}
      ${Else}
        StrCpy $R4 "$R4G"
      ${EndIf}
    ${Else}
      StrCpy $R4 "$R4M"
    ${EndIf}
  ${Else}
    StrCpy $R4 "$R4K"
  ${EndIf} 
FunctionEnd


#--------------------------------------------#
# Get human-readable file system type from $R8 to $R8
Function GetFSType
IntFmt $R8 "%02X" $R8
      ${Select} $R8
        ${Case} "01"
          StrCpy $R8 "FAT12"
        ${Case3} "04" "06" "0E"
          StrCpy $R8 "FAT16"
        ${Case} "07"
          StrCpy $R8 "NTFS/exFAT"
        ${Case2} "0B" "0C"
          StrCpy $R8 "FAT32"
        ${Case} "11"
          StrCpy $R8 "hidden FAT12"
        ${Case3} "14" "16" "1E"
          StrCpy $R8 "hidden FAT16"
        ${Case} "17"
          StrCpy $R8 "hidden NTFS/exFAT"
        ${Case2} "1B" "1C"
          StrCpy $R8 "hidden FAT32"
        ${Case} "27"
          StrCpy $R8 "hidden NTFS (RE)"
        ${Case} "35"
          StrCpy $R8 "JFS"
        ${Case2} "80" "81"
          StrCpy $R8 "MINIX"
        ${Case} "82"
          StrCpy $R8 "LINUX SWAP"
        ${Case} "83"
          StrCpy $R8 "LINUX"
        ${CaseElse}
          StrCpy $R8 "UNSUPPORTED"
      ${EndSelect}
      
FunctionEnd


#--------------------------------------------#
# Patch $FileToPatch using $TargetFile as $PatchedFile
Function PatchFileName

  System::Store "S"

  # determine signature offset
  ${GetFileName} $FileToPatch $R0
  ${If} $R0 == "bootlace.com"
    IntOp $R0 0 + 0x8FFC
  ${ElseIf} $R0 == "grldr.mbr"
    IntOp $R0 0 + 0x23FC
  ${Else}
    IntOp $R0 0 + 0x1FFC
  ${EndIf}

  # Read source file
  StrCpy $R1 $FileToPatch
  System::Call "kernel32::CreateFile(t R1, i ${GENERIC_READ}, i ${FILE_SHARE_READ}, i 0, \
                                     i ${OPEN_EXISTING}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R1"
  ${If} $R1  $R2
    MessageBox MB_OK|MB_ICONEXCLAMATION "$FileToPatch 文件不是一个有效的 Grub4DOS 文件"
    System::Call "kernel32::CloseHandle(i R1)"
    Goto lblpatchfilename1 
  ${EndIf}
  System::Alloc $R2
  Pop $R3
  System::Call "kernel32::ReadFile(i R1, i R3 , i R2, *i .R4, i ..)"
  System::Call "kernel32::CloseHandle(i R1)"
  ${If} $R2 != $R4
    MessageBox MB_OK|MB_ICONEXCLAMATION "$FileToPatch 读取错误!"
lblpatchfilename:
    System::Free $R3
lblpatchfilename1:
    System::Store "L"
    Push "error"
    Return
  ${EndIf}
   
  IntOp $R4 $R3 + $R0
  System::Call "*$R4(i .R5)"
  IntFmt $R5 "0x%08X" $R5
  ${If} $R5 != "0xAA555247"
    MessageBox MB_OK|MB_ICONEXCLAMATION "$FileToPatch 文件不是一个有效的Grub4DOS文件"
    Goto lblpatchfilename1
  ${EndIf}

  ${GetFileName} $FileToPatch $R0
  ${If} $R0 == "bootlace.com"
    IntOp $R0 0 + 0x7000
  ${Else}
    IntOp $R0 0 + 0x400
  ${EndIf}
  
  # FAT32
  IntOp $R4 $R0 + 0x1EC
  IntOp $R4 $R3 + $R4
  System::Call "*$R4(&i2 .R5)"
  IntOp $R5 $R5 & 0x7FF
  IntOp $R4 $R0 + $R5
  IntOp $R4 $R3 + $R4
  StrCpy $R5 $TargetFile
  ${WordReplace} $R5 "." "" "+" $R5
  ${StrFilter} $R5 "+" "" "" $R5
  StrLen $R6 $R5
  System::Call "*(&t12 R5) i .R7"
  System::Copy /$R6 $R4 $R7

  # FAT12/16
  IntOp $R0 $R0 + 0x200
  IntOp $R4 $R0 + 0x1EC
  IntOp $R4 $R3 + $R4
  System::Call "*$R4(&i2 .R5)"
  IntOp $R5 $R5 & 0x7FF
  IntOp $R4 $R0 + $R5
  IntOp $R4 $R3 + $R4
  System::Copy /$R6 $R4 $R7  

  System::Free $R7

  # EXT2
  IntOp $R0 $R0 + 0x200
  IntOp $R4 $R0 + 0x1EE
  IntOp $R4 $R3 + $R4
  System::Call "*$R4(&i2 .R5)"
  IntOp $R5 $R5 & 0x7FF
  IntOp $R4 $R0 + $R5
  IntOp $R4 $R3 + $R4
  StrCpy $R5 $TargetFile
  ${StrFilter} $R5 "-" "" "" $R5
  StrLen $R6 $R5
  System::Call "*(&t12 R5) i .R7"
  System::Copy /$R6 $R4 $R7
  
  # NTFS
  IntOp $R0 $R0 + 0x200
  IntOp $R4 $R0 + 0x1EC
  IntOp $R4 $R3 + $R4
  System::Call "*$R4(&i2 .R5)"
  IntOp $R5 $R5 & 0x7FF
  IntOp $R4 $R0 + $R5
  IntOp $R4 $R3 + $R4
  System::Copy /$R6 $R4 $R7
  
  System::Free $R7
  
  # Write output file
  StrCpy $R1 $PatchedFile
  System::Call "kernel32::CreateFile(t R1, i ${GENERIC_WRITE}, i 0, i 0, i ${CREATE_ALWAYS}, \
                                     i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R1"
  ${If} $R1  0
    System::Call "kernel32::ReadFile(i R5, i R6 , i R7, *i .R4, i ..)"
    System::Call "kernel32::CloseHandle(i R5)"        
    System::Call /NOUNLOAD 'user32::SendMessage(i $TextBox, i ${WM_SETTEXT}, i 0, i R6)'      
  ${EndIf}
  System::Free $R6
FunctionEnd


#--------------------------------------------#
# Check file name ($R0) validity by creating a file in TempFolder; Set $R0 to null if error
Function CheckFileName
  Push $R2
  Push $R1
  ${If} $R0 != ""
    StrCpy $R1 $TempFolder\$R0
    System::Call "kernel32::CreateFile(t R1, i ${GENERIC_READ}|${GENERIC_WRITE}, i 0, i 0, \
                                       i ${CREATE_ALWAYS}, i ${FILE_ATTRIBUTE_NORMAL}, i 0) i .R2"
    ${If} $R2 > 0
      System::Call "kernel32::CloseHandle(i R2)"
      Delete $R1
      Goto lblcheckfilename
    ${EndIf}
  ${EndIf}
  StrCpy $R0 ""
  MessageBox MB_OK|MB_ICONEXCLAMATION "你必须提供一个有效的文件名"
lblcheckfilename:
  Pop $R1
  Pop $R2
FunctionEnd

#--------------------------------------------#
# Write log messages from $R0
Function WriteLog
  ${GetTime} "" L $0 $1 $2 $3 $4 $5 $6
  StrCpy $msg "--------------------------------$\r$\n$2-$1-$0 $4:$5:$6"
  IfFileExists $LogFile +3 0
    FileOpen $0 $LogFile w
    GoTo +2
  FileOpen $0 $LogFile a
  FileSeek $0 0 END
  FileWrite $0 "$msg$\r$\n$R0$\r$\n"
  FileClose $0
FunctionEnd


#--------------------------------------------#
Function CleanUp
  RMDir /r /REBOOTOK $TempFolder
FunctionEnd

#--------------------------------------------#
!insertmacro MUI_LANGUAGE "English"