@ECHO OFF
SETLOCAL

REM  Script for generation of rc VERSIONINFO & StringFileInfo

REM ====================
REM Installation Variables
REM ====================
:: VERSION_FILE - Untracked file to be included in packaged source releases.
::                it should contain a single line in the format:
::                $Project_Name VERSION $tag (ie: Foobar VERSION v1.0.0-alpha0)
SET VERSION_FILE=GIT-VS-VERSION-FILE

:: DEFAULT_VERSION - Version string to be processed when neither Git nor a
::                packed version file is available.
SET DEFAULT_VERSION=v1.4.0-NoGit

:: COUNT_PATCHES_FROM - Determines which tag to count the number of patches from
::                for the final portion of the digital version number.
::                Valid values are:
::                   major - count from earliest Major.0.0* tag.
::                   minor - count from earliest Major.Minor.0* tag.
::                   fix   - count from earliest Major.Minor.Fix tag.
SET COUNT_PATCHES_FROM=fix

:: USES_PRERELEASE_TAGS - numeric bool value to determine if GET_GIT_PATCHES
::                function should read the number of patches in the format of
::                  (Default) 1 - Major.Minor.Fix-Stage#-'CommitCount'
::                            0 - Major.Minor.Fix-'CommitCount'
SET USE_PRERELEASE_TAGS=1

:: --------------------
:CHECK_ARGS
:: --------------------

:: Console output only.
IF [%1] == [] GOTO START

IF "%~1" == "--help" GOTO USAGE
IF "%~1" == "--quiet" SET fQUIET=1& SHIFT
IF "%~1" == "--force" SET fFORCE=1& SHIFT

:: Un-documented switch
IF "%~1" == "--test" GOTO TEST

IF EXIST %~1\NUL (
  :: %1 is a path
  SET CACHE_FILE=%~s1\%VERSION_FILE%
  SHIFT
)

IF [%~nx1] NEQ [] (
  :: %1 is a file
  SET HEADER_OUT_FILE=%~fs1
  SHIFT
)
:: This should always be the last argument.
IF [%1] NEQ [] GOTO USAGE

:: Some basic sanity checks.
IF DEFINED fQUIET (
  IF NOT DEFINED HEADER_OUT_FILE GOTO USAGE
)

IF DEFINED CACHE_FILE (
  SET CACHE_FILE=%CACHE_FILE:\\=\%
  IF NOT DEFINED HEADER_OUT_FILE GOTO USAGE
)
GOTO START

:: --------------------
:USAGE
:: --------------------
ECHO usage: [--help] ^| ^| [--quiet] [--force] [CACHE PATH] [OUT FILE]
ECHO.
ECHO  When called without arguments version information writes to console.
ECHO.
ECHO  --help      - displays this output.
ECHO.
ECHO  --quiet     - Suppress console output.
ECHO  --force     - Ignore cached version information.
ECHO  CACHE PATH  - Path for non-tracked file to store git-describe version.
ECHO  OUT FILE    - Path to writable file that is included in the project's rc file.
ECHO.
ECHO  Version information is expected to be in the format: vMajor.Minor.Fix[-stage#]
ECHO  Where -stage# is alpha, beta, or rc. ( example: v1.0.0-alpha0 )
ECHO.
ECHO  Example pre-build event:
ECHO  CALL $(SolutionDir)..\scripts\GIT-VS-VERSION-GEN.bat "$(IntDir)\" "$(SolutionDir)..\src\gen-versioninfo.h"
ECHO.
GOTO END


REM ===================
REM Entry Point
REM ===================
:START
ECHO.
CALL :INIT_VARS
CALL :GET_VERSION_STRING
IF DEFINED fGIT_AVAILABLE (
  IF DEFINED fLEAVE_NOW GOTO END
  IF DEFINED CACHE_FILE (
    CALL :CHECK_CACHE
  )
)
IF DEFINED fLEAVE_NOW GOTO END
CALL :SET_BUILD_PARTS
CALL :PREP_OUT
CALL :WRITE_OUT
GOTO END

REM ====================
REM FUNCTIONS
REM ====================
:: --------------------
:INIT_VARS
:: --------------------
:: The following variables are used for the final version output.
::    String Version:  Major.Minor.Fix.Stage#[.Patches.SHA1[.dirty]]
SET strFILE_VERSION=

::    Digital Version: Major, Minor, Fix, Patches
SET nbMAJOR_PART=0
SET nbMINOR_PART=0
SET nbFIX_PART=0
SET nbPATCHES_PART=0

:: VERSIONINFO VS_FF_ flags
SET fPRIVATE=0
SET fPATCHED=0
SET fPRE_RELEASE=0

:: Supporting StringFileInfo - not used for clean release builds.
SET strPRIVATE_BUILD=
SET strCOMMENT=

GOTO :EOF

:: --------------------
:GET_VERSION_STRING
:: --------------------
:: Precedence is Git, VERSION_FILE, then DEFAULT_VERSION.
:: Check if git is available by testing git describe.
CALL git describe>NUL 2>&1
IF NOT ERRORLEVEL 1 (
  SET fGIT_AVAILABLE=1
  :: Parse git version string
  CALL :PARSE_GIT_STRING
) ELSE (
  :: Use the VERSION_FILE if it exists.
  IF EXIST "%VERSION_FILE%" (
    FOR /F "tokens=3" %%A IN (%VERSION_FILE%) DO (
      SET strFILE_VERSION=%%A
    )
  ) ELSE (
    :: Default to the DEFAULT_VERSION
    SET strFILE_VERSION=%DEFAULT_VERSION%
  )
)
SET strFILE_VERSION=%strFILE_VERSION:~1%
SET strFILE_VERSION=%strFILE_VERSION:-=.%
GOTO :EOF

:: --------------------
:PARSE_GIT_STRING
:: --------------------
FOR /F "tokens=*" %%A IN ('"git describe --long --tags --dirty "') DO (
  SET strFILE_VERSION=%%A
)
echo %strFILE_VERSION%
:: If HEAD is dirty then this is not part of an official build and even if a
:: commit hasn't been made it should still be marked as dirty and patched.
SET tmp=
GOTO :EOF

:: --------------------
:CHECK_CACHE
:: --------------------
:: Exit early if a cached git built version matches the current version.
IF DEFINED HEADER_OUT_FILE (
  IF EXIST "%HEADER_OUT_FILE%" (
    IF [%fFORCE%] EQU [1] DEL "%CACHE_FILE%"
    IF EXIST "%CACHE_FILE%" (
      FOR /F "tokens=*" %%A IN (%CACHE_FILE%) DO (
        IF "%%A" == "%strFILE_VERSION%" (
          IF NOT DEFINED fQUIET (
            ECHO Build version is assumed unchanged from: %strFILE_VERSION%.
          )
          SET fLEAVE_NOW=1
        )
      )
    )
  )

  ECHO %strFILE_VERSION%> "%CACHE_FILE%"
)
GOTO :EOF

:: --------------------
:SET_BUILD_PARTS
:: --------------------
:: The min version is X.Y.Z and the max is X.Y.Z.Stage#.Commits.SHA.dirty
:: strTMP_STAGE_PART is a holder for anything past 'X.Y.Z.'.
FOR /F "tokens=1,2,3,* delims=." %%A IN ("%strFile_Version%") DO (
  SET nbMAJOR_PART=%%A
  SET nbMINOR_PART=%%B
  SET nbFIX_PART=%%C
  SET strTMP_STAGE_PART=%%D
)
GOTO :EOF

:: --------------------
:PREP_OUT
:: --------------------
SET csvFILE_VERSION=%nbMAJOR_PART%,%nbMINOR_PART%,%nbFIX_PART%,%nbPATCHES_PART%
SET hexFILE_VERSION=
CALL :SET_HEX

IF NOT %fPRIVATE% EQU 0 SET fPRIVATE=VS_FF_PRIVATEBUILD
IF NOT %fPATCHED% EQU 0 SET fPATCHED=VS_FF_PATCHED
IF NOT %fPRE_RELEASE% EQU 0 SET fPRE_RELEASE=VS_FF_PRERELEASE
GOTO :EOF

:: --------------------
:SET_HEX
:: --------------------
:: Iterate Major, Minor, Fix, Patches as set in csvFILEVERSION and convert to
:: hex while appending to the hexFILE_VERION string to give a padded 32bit
:: end result. ie: v1.0.1.34 = 0x0001000000010022
SET hex_values=0123456789ABCDEF

FOR /F "tokens=1-4 delims=," %%A IN ("%csvFILE_VERSION%") DO (
  CALL :int2hex %%A
  CALL :int2hex %%B
  CALL :int2hex %%C
  CALL :int2hex %%D
)

SET hexFILE_VERSION=0x%hexFILE_VERSION%
SET hex_values=

GOTO :EOF

:int2hex
SETLOCAL ENABLEDELAYEDEXPANSION
SET /A pad=4
SET /A iVal=%1

:hex_loop
SET /A pad=%pad% - 1
SET /A hVal=%iVal% %% 16
SET hVal=!hex_values:~%hVal%,1!
SET hex_word=%hVal%%hex_word%
SET /A iVal=%iVal% / 16
IF %iVal% GTR 0 GOTO hex_loop

:hex_pad_loop
FOR /L %%A in (1,1,%pad%) DO SET hex_word=0!hex_word!
ENDLOCAL& SET hexFILE_VERSION=%hexFILE_VERSION%%hex_word%
GOTO :EOF

:: --------------------
:WRITE_OUT
:: --------------------
:: HEADER_OUT falls through to CON_OUT which checks for the QUIET flag.
IF DEFINED HEADER_OUT_FILE (
  CALL :OUT_HEADER
) ELSE (
  IF NOT DEFINED TESTING (
  CALL :CON_OUT
  ) ELSE (
    CALL :TEST_OUT
  )
)
GOTO :EOF

:: --------------------
:OUT_HEADER
:: --------------------
ECHO unsigned short ozw_vers_major = %nbMAJOR_PART%; >> "%HEADER_OUT_FILE%"
ECHO unsigned short ozw_vers_minor = %nbMINOR_PART%; >> "%HEADER_OUT_FILE%"
ECHO unsigned short ozw_vers_revision = %nbFIX_PART%; >> "%HEADER_OUT_FILE%"
ECHO char ozw_version_string[] = "%strFILE_VERSION%\0"; >> "%HEADER_OUT_FILE%"


SET nbMAJOR_PART=0
SET nbMINOR_PART=0
SET nbFIX_PART=0
SET nbPATCHES_PART=0

:: --------------------
:CON_OUT
:: --------------------
IF DEFINED fQUIET GOTO :EOF
ECHO Version String:       %strFILE_VERSION%
ECHO Digital Version ID:   %csvFILE_VERSION%
ECHO Hex Version ID:       %hexFILE_VERSION%
GOTO :EOF


:: --------------------
:END
:: --------------------
