; Original script from http://www.autohotkey.com/forum/topic8618.html

; changelog :
; added FileVersion and ProductVersion extraction formatted as X,X,X,X (for direct use in RC file)
; added BuildID extraction

oldErrMode:=dllCall("SetErrorMode",uint,1) ; SEM_FAILCRITICALERRORS=0x0001

FileGetFullVer(file,verFlags=1,delim="")               ;;
{  ifEqual,delim,,delim=|
; FLAGS: 0x00001 - numeric info
;        0x00002 - file type

;        0x10000 - numeric info (FileVersion) formatted as X,X,X,X
;        0x20000 - numeric info (ProductVersion) formatted as X,X,X,X

;        0x00004 - file description
;        0x00008 - company name
;        0x00010 - FileVersion
;        0x00020 - Comments
;        0x00040 - InternalName
;        0x00080 - LegalCopyright
;        0x00100 - LegalTrademarks
;        0x00200 - OriginalFilename
;        0x00400 - ProductName
;        0x00800 - ProductVersion
;        0x01000 - PrivateBuild
;        0x02000 - SpecialBuild
;        0x04000 - LegalTrademarks1

;        0x08000 - clean-up empty fields

;        0x40000 - BuildID


; VersionInfoStrings
;   viPredefinedFirst = 0
;   viLanguage = 0
;   viComments = 1
;   viCompanyName = 2
;   viFileDescription = 3
;   viFileVersion = 4
;   viInternalName = 5
;   viLegalCopyright = 6
;   viLegalTrademarks = 7
;   viOriginalFilename = 8
;   viProductName = 9
;   viProductVersion = 10
;   viPrivateBuild = 11
;   viSpecialBuild = 12
;   viLegalTrademarks1 = 13  'Used by Office apps only?
;   viLegalTrademarks2 = 14  'Used by Office apps only?
;   viPredefinedLast = 14

;typedef struct tagVS_FIXEDFILEINFO
;    0  DWORD dwSignature;
;    4  DWORD dwStrucVersion;
;    8  DWORD dwFileVersionMS;
;    12 DWORD dwFileVersionLS;
;    16 DWORD dwProductVersionMS;
;    20 DWORD dwProductVersionLS;
;    24 DWORD dwFileFlagsMask;
;    28 DWORD dwFileFlags;
;    32 DWORD dwFileOS;
;    36 DWORD dwFileType;
;    40 DWORD dwFileSubtype;
;    44 DWORD dwFileDateMS;
;    48 DWORD dwFileDateLS;

;   MAX_PATH = 260
;   ; ----- VS_VERSION.dwFileFlags -----
;   VS_FFI_SIGNATURE = 0xFEEF04BD
;   VS_FFI_STRUCVERSION = 0x10000
;   VS_FFI_FILEFLAGSMASK = 0x3F
;   ; ----- VS_VERSION.dwFileFlags -----
;   VS_FF_DEBUG = 0x1
;   VS_FF_PRERELEASE = 0x2
;   VS_FF_PATCHED = 0x4
;   VS_FF_PRIVATEBUILD = 0x8
;   VS_FF_INFOINFERRED = 0x10
;   VS_FF_SPECIALBUILD = 0x20
;   ; ----- VS_VERSION.dwFileOS -----
;   VOS_UNKNOWN = 0x0
;   VOS_DOS = 0x10000
;   VOS_OS216 = 0x20000
;   VOS_OS232 = 0x30000
;   VOS_NT = 0x40000
;   VOS_DOS_WINDOWS16 = 0x10001
;   VOS_DOS_WINDOWS32 = 0x10004
;   VOS_OS216_PM16 = 0x20002
;   VOS_OS232_PM32 = 0x30003
;   VOS_NT_WINDOWS32 = 0x40004
;   ; ----- VS_VERSION.dwFileType -----
;   VFT_UNKNOWN = 0x0
;   VFT_APP = 0x1
;   VFT_DLL = 0x2
;   VFT_DRV = 0x3
;   VFT_FONT = 0x4
;   VFT_VXD = 0x5
;   VFT_STATIC_LIB = 0x7
;   ; **** VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT ****
;   VFT2_FONT_RASTER = 0x1
;   VFT2_FONT_VECTOR = 0x2
;   VFT2_FONT_TRUETYPE = 0x3
;   ; ----- VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV -----
;   VFT2_UNKNOWN = 0x0
;   VFT2_DRV_PRINTER = 0x1
;   VFT2_DRV_KEYBOARD = 0x2
;   VFT2_DRV_LANGUAGE = 0x3
;   VFT2_DRV_DISPLAY = 0x4
;   VFT2_DRV_MOUSE = 0x5
;   VFT2_DRV_NETWORK = 0x6
;   VFT2_DRV_SYSTEM = 0x7
;   VFT2_DRV_INSTALLABLE = 0x8
;   VFT2_DRV_SOUND = 0x9
;   VFT2_DRV_COMM = 0xA

   version=
   dummy:=1
   fiSize:=dllCall("version\GetFileVersionInfoSizeA",str,file,uint,&dummy)
   varSetCapacity(fi,fiSize,0)
   loop,1
   {  if !dllCall("version\GetFileVersionInfoA",str,file, int,0, int,fiSize, uint,&fi)
         break
      if !dllCall("version\VerQueryValueA",uint,&fi, str,"\", uintp,fiFFI#, uintp,dummy)
         break
      varSetCapacity(fiFFI,13*4)
      dllCall("RtlMoveMemory",uint,&fiFFI,uint,fiFFI#,uint,13*4)
      version:=iif(verFlags & 1=0,""
                  ,"v" extractInteger(fiFFI,10,0,2) "." extractInteger(fiFFI,8,0,2)
                  . iif(extractInteger(fiFFI,12,0,2)=0,""
                        ,"." extractInteger(fiFFI,14,0,2) "." extractInteger(fiFFI,12,0,2)))
             . iif(verFlags & 2=0,""
                  ,"|" switch(extractInteger(fiFFI,36)+1,""
                        ,"Application","DLL"
                        ,"|" switch(extractInteger(fiFFI,40)+1,"","Printer ","Keyboard "
                              ,"Language ","Display ","Mouse ","Network ","System "
                              ,"Installable ", "Sound ","Comm. ") "driver"
                        ,"|" switch(extractInteger(fiFFI,40),"Raster","Vector","TrueType") " font"
                        ,"|VxD driver", "|Static Lib"))
             . iif(verFlags & 0x10000,"|"  extractInteger(fiFFI,10,0,2) "," extractInteger(fiFFI,8,0,2) "," extractInteger(fiFFI,14,0,2) "," extractInteger(fiFFI,12,0,2),"")
             . iif(verFlags & 0x20000,"|"  extractInteger(fiFFI,18,0,2) "," extractInteger(fiFFI,16,0,2) "," extractInteger(fiFFI,22,0,2) "," extractInteger(fiFFI,20,0,2),"")
      if (verFlags & 0xFFFE)
      {  if !dllCall("version\VerQueryValueA",uint,&fi, str,"\VarFileInfo\Translation", uintp,fiTrans#, uintp,dummy)
            break
         ifEqual,dummy,0, break
         fiTrans:=0
         dllCall("RtlMoveMemory",uintP,fiTrans,uint,fiTrans#,uint,4)

         Lang#:=fiTrans & 0xFFFF
         CP#:=fiTrans>>16

         varSetCapacity(lang,256,0)
         dummy:=dllCall("VerLanguageNameA",uint,Lang#, str,lang, uint,256)
         stringLeft,lang,lang,%dummy%

         sSubBlock:= "\StringFileInfo\" FmtHex(Lang#,4) . FmtHex(CP#,4) "\"
         Company:=verGetStdValue(fi,sSubBlock "CompanyName")
         if !Company
         {  ; Try U.S. English...?
            dummy:="\StringFileInfo\0409" FmtHex(CP#,4) "\"
            Company:=verGetStdValue(fi,dummy "CompanyName")
            if (Company)
               sSubBlock:=dummy ; We probably found the MS version bug.
         }

         version:=version
            . iif(verFlags & 4=0,"","|" verGetStdValue(fi,sSubBlock "FileDescription"))
            . iif(verFlags & 8=0,"","|" Company)
            . iif(verFlags & 0x00010,"|" verGetStdValue(fi,sSubBlock "FileVersion"),"")
            . iif(verFlags & 0x00020,"|" verGetStdValue(fi,sSubBlock "Comments"),"")
            . iif(verFlags & 0x00040,"|" verGetStdValue(fi,sSubBlock "InternalName"),"")
            . iif(verFlags & 0x00080,"|" verGetStdValue(fi,sSubBlock "LegalCopyright"),"")
            . iif(verFlags & 0x00100,"|" verGetStdValue(fi,sSubBlock "LegalTrademarks"),"")
            . iif(verFlags & 0x00200,"|" verGetStdValue(fi,sSubBlock "OriginalFilename"),"")
            . iif(verFlags & 0x00400,"|" verGetStdValue(fi,sSubBlock "ProductName"),"")
            . iif(verFlags & 0x00800,"|" verGetStdValue(fi,sSubBlock "ProductVersion"),"")
            . iif(verFlags & 0x01000,"|" verGetStdValue(fi,sSubBlock "PrivateBuild"),"")
            . iif(verFlags & 0x02000,"|" verGetStdValue(fi,sSubBlock "SpecialBuild"),"")
            . iif(verFlags & 0x04000,"|" verGetStdValue(fi,sSubBlock "LegalTrademarks1")
                                  . "|" verGetStdValue(fi,sSubBlock "LegalTrademarks2"),"")
            . iif(verFlags & 0x40000,"|" verGetStdValue(fi,sSubBlock "BuildID"),"")
      }
   }
   if (verFlags & 0x08000)
   {  dummy=
      version=|%version%|
      stringReplace,version,version,|v0.0|
      loop,parse,version,|
         if (A_LoopField)
            dummy:=dummy . delim . A_LoopField
      return strMid(dummy,2)
   }
   if (delim<>"|")
      stringReplace,version,version,|,%delim%,all
   return version
}

verGetStdValue(byref fi, value)        ;;
{  fiValue#:=0
   dummy:=0
   if !dllCall("version\VerQueryValueA",str,fi, str,value, uintp,fiValue#, uintp,dummy)
      return
   ifEqual,dummy,0, return

   len:=dllCall("lstrlenA",uint,fiValue#)
   varSetCapacity(fiValue,len+1,0)
   dllCall("RtlMoveMemory",str,fiValue, uint,fiValue#, uint,len)

      __trim:=A_AutoTrim
      AutoTrim,on
   fiValue=%fiValue%
      AutoTrim,%__trim%

   return fiValue
}
fmtHex(num,digits=8)                  ;; without "0x" padded with "0" ;;
{  varSetCapacity(s,digits+8,asc("0"))
   __format:=A_FormatInteger
   setformat,integer,hex
   num+=0
   s:=s . num
   stringReplace,s,s,0x
   setformat,integer,%__format%
   return strRight(s,digits)
}
iif(expr, a, b)                            ;;
{
   if (expr)
      return a
   else
      return b
}
strLeft(s, n)                            ;;
{
   stringLeft,s,s,%n%
   return s
}
strRight(s, n)                            ;;
{
   stringRight,s,s,%n%
   return s
}
strMid(s, begin, n=0x7FFF, Left=0) ; if L<>0 then mid to the left  ;;
{
   if Left=0
      stringMid,s,s,%begin%,%n%
   else
      stringMid,s,s,%begin%,%n%,L
   return s
}
ExtractInteger(ByRef pSource, pOffset = 0, pIsSigned = false, pSize = 4) ;;
; pSource is a string (buffer) whose memory area contains a raw/binary integer at pOffset.
; The caller should pass true for pSigned to interpret the result as signed vs. unsigned.
; pSize is the size of PSource's integer in bytes (e.g. 4 bytes for a DWORD or Int).
; pSource must be ByRef to avoid corruption during the formal-to-actual copying process
; (since pSource might contain valid data beyond its first binary zero).
{
   Loop %pSize%  ; Build the integer by adding up its bytes.
      result += *(&pSource + pOffset + A_Index-1) << 8*(A_Index-1)
   if (!pIsSigned OR pSize > 4 OR result < 0x80000000)
      return result  ; Signed vs. unsigned doesn't matter in these cases.
   ; Otherwise, convert the value (now known to be 32-bit) to its signed counterpart:
   return -(0xFFFFFFFF - result + 1)
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4) ;;
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", UInt, &pDest + pOffset + A_Index-1, UInt, 1, UChar, pInteger >> 8*(A_Index-1) & 0xFF)
}

switch(idx,val1="",val2="",val3="",val4="",val5="",val6="",val7="",val8=""
          ,val9="",val10="",val11="",val12="",val13="",val14="",val15="",val16="")   ;;
{
   return val%idx%
} 
