Nov 9, 2013

Function: Dec2Hex

The quick way which makes use of a feature that you might not necessarily have known, which is in the REG command: when writing a DWORD/QWORD value and giving data in decimal, it converts the input to hexadecimal for you.
If the OS is 32-bit, over-32-bit-values are not supported. When 32-bit, if it's given with a value that is greater than or equal to 4294967295 (232-1), then it returns 0xFFFFFFFF.
If the OS is 64-bit, and if the input is greater than 18446744073709551615 (264-1), the function fails.
@echo off
:loop
echo.
set/p input=give a decimal number 
call:Dec2Hex input output
if %errorlevel%==0 (echo  %input% --^> %output%)
if %errorlevel%==1 (echo  Could not carry out conversion.)
goto loop
:Dec2Hex
setlocal
set v0=%% %1 %%
set v0=%v0: =%
call set num=%v0%
set err=0
set arch=64
set v0=%processor_architecture%
set v0=%v0:86=%
if not "%v0%"=="%processor_architecture%" (set/a arch/=2)
if "%arch%"=="32" (set len=D) else (set len=Q)
reg add "HKCU" /v "dec2hex_removeme" /t "REG_%len%WORD" /d "%num%" /f>nul 2>&1||set err=1
if not "%err%"=="1" (for /f "tokens=3" %%A in ('reg query "HKCU" /v "dec2hex_removeme"') do (set hex=%%A))
if defined hex (reg delete "HKCU" /v "dec2hex_removeme" /f>nul 2>&1)
endlocal&set %2=%hex%&exit/b%err%

Nov 2, 2013

Function: ShowPercentageOfProgress

Back in January I wrote the prototype of percentage calculation routine in batch. It was an extremely sluggish proof of concept. Well, as I returned to reviewing it, I noticed I would change a lot. In the meantime, I have found another method to detect integer overflow, what allowed me to greatly improve performance of this routine. I also changed progress bar drawing routine, in the older one it was called as many times as did the whole percentage calculation procedure. The enhanced one carries out at most 100 calls. Furthermore, the newer one supports one-percent changes the way the progress bar does not need complete redrawing, only 1 byte gets changed. In the older one, you used to need to be cautious with denominator when initializing the function:
1/10 -> 10%, 10 kept as the denominator until it arrives to 10/10
2/10 -> 20%
3/10 -> 30%
301/1001 -> is: 3010% (error, because 10 is still being kept as a denominator), should be: 30,0699(300699)
Newer version abolishes the above behavior, that is, denominator is not kept for further calculations, it is received every time the function is called.

The function can receive any unsigned integer numbers within the range of 0 to 1e+7, whilst the denominator cannot be 0. That means, percents can "go back" as you change values "on the fly", it could be 17% first, and 15% after you increase the denominator during the execution. The picture below presents the same execution flow.

The progress bar is started drawing from 1%.
I worked out two variants of the improved function. The first one shows only whole percents, and the other one - more exact percents with mantissa. Rounding up is not carried out anymore.

With mantissa:

Without mantissa:


I need not add that the one without calculating mantissa is less CPU-expensive, and is faster. As you might have noticed, the "bigger" the fraction is, the shorter the decimal expansion gets to be, to completely disappear at some point.
Both the variants have some kind of demonstration in themselves, just run one of them and type "demo". You can look what does it do below.
The one that calculates mantissa...
@echo off
setlocal EnableDelayedExpansion
prompt $s
echo.
echo Usage:
echo.
echo ShowPercentageOfProgressInWindowTitle ^<stage^> ^<end^> trimtrailingzeros_[yes^|no] updateprogressbar_[yes^|no]
echo.
echo Example: ShowPercentageOfProgressInWindowTitle 7 11 trimtrailingzeros_yes updateprogressbar_no
echo Lack of the updateprogressbar_* parameter defaults to progress bar drawing OFF as well as
echo lack of the trimtrailingzeros_* parameter defaults to trailing zeros trimming OFF.
:loop
echo.
set ch_=%ch%
set ch=&set/p ch= give parameters :
if defined ch_ (for /f "tokens=1,2" %%A in ("%ch_%") do (for /f "tokens=1,2" %%C in ("%ch%") do (if "%%A"=="%%C" (if "%%B"=="%%D" (echo.&echo _PREV_PERCENT == PERCENT -^> to reduce performance expensiveness, progress bar&echo state stays halted until the routine is given with a different fraction)))))
::for /l %%G in (0,17034,9998958) do (call:ShowPercentageOfProgressInWindowTitle %%G 9998958 updateprogressbar_yes)
if /i "%ch%"=="demo" (call:demo) else (call:ShowPercentageOfProgressInWindowTitle %ch%)
if "%errorlevel%"=="1" (echo.&echo Too few parameters.)
if "%errorlevel%"=="2" (echo.&echo Integer overflow, or divisor ^>1e+7, or progress is over 100%%.)
if "%errorlevel%"=="3" (echo.&echo Division by zero detected.)
goto loop
goto:eof
:demo
::counts size of all files in system32 root dir...
for %%A in (%windir%\system32\*.*) do (set/a exact_size+=%%~zA)
::floors bytes
set _exact_size=%exact_size:~0,-3%
:: ...and re-enumerates the directory to gradually add next files' sizes...
for %%A in (%windir%\system32\*.*) do (
set/a var+=%%~zA
:: ...to show the behaviour of the function
call:ShowPercentageOfProgressInWindowTitle !var:~0,-3! %_exact_size% updateprogressbar_yes
)
exit/b0
:UpdateProgressBar
setlocal EnableDelayedExpansion
if not defined _prev_percent (goto upb_)
for /f "tokens=1 delims=," %%A in ("%percent%") do (set p_base=%%A)
for /f "tokens=1 delims=," %%A in ("%_prev_percent%") do (set pp_base=%%A)
set/a diff=%p_base%-%pp_base%
if "%diff%"=="0" (goto upb_endproc)
::if there is a difference of 1 in whole percents between next calls of this function, the string-substitution feature is used just to replace ": " with "::", not to redraw the whole bar
if "%diff%"=="1" (for /f "tokens=1 delims=," %%A in ("%percent%") do (if not "%diff%"=="%%A" (set _titlepat=%_titlepat:: =::%&goto upb_endproc)))
:upb_
if not "%1"=="0" (goto upbproc)
set _titlepat=
goto upb_endproc
:upbproc
set _titlepat=[
for /l %%G in (1,1,%1) do (set _titlepat=!_titlepat!:)
set/a _significant=100-%1
if "%_significant%"=="0" (goto titlepat_enclose)
for /l %%G in (1,1,%_significant%) do (set _titlepat=!_titlepat! )
:titlepat_enclose
set _titlepat=%_titlepat%]
:upb_endproc
endlocal&set _titlepat=%_titlepat%&exit/b0

:ShowPercentageOfProgressInWindowTitle
setlocal
set frac=%1 %2
if defined trimtrailingzeros (
if defined updateprogressbar (
:spopiwt_invoke
call:spopiwt_proc %frac%||goto spopiwt_process_is_over
if defined hundred_reached (goto spopiwt_process_is_over)
::works semi-locally, assists itself with two variables that are set globally, to reduce progress bar drawing routine calls
endlocal&set _prev_percent=%percent%&set _titlepat=%_titlepat%&exit/b%spopiwt_returned%
)
)
shift
shift
:paramloop
if not defined trimtrailingzeros (if /i "%1"=="trimtrailingzeros_yes" (set trimtrailingzeros=yes) else (set trimtrailingzeros=no))
if not defined updateprogressbar (if /i "%1"=="updateprogressbar_yes" (set updateprogressbar=yes) else (set updateprogressbar=no))
shift
if not "%1"=="" goto paramloop
goto spopiwt_invoke
:spopiwt_process_is_over
endlocal&exit/b%spopiwt_returned%
:spopiwt_proc
::receives:
::
:: %1 - dividend (x%)
:: %2 - divisor (100%)
::
::exitcodes:
:: 0 = success calculating and setting everything
:: 1 = too few parameters
:: 2 = bad input (L>R )
:: 3 = division-by-zero error
::
set _left=%1
set _right=%2
if defined _left (if defined _right (goto spopiwt_continue))
set exitcode=1
goto spopiwt_endproc
:spopiwt_continue
if not [%_right:~7%]==[] (if not [%_right%]==[10000000] (set exitcode=2&goto spopiwt_endproc))
if %_right% EQU 0 (set exitcode=3&goto spopiwt_endproc)
if %_left% GEQ %_right% (set percent_tmp=100&set hundred_reached=true&goto spopiwt_setpercent)
if %_left% EQU 0 (set percent_tmp=0&goto spopiwt_setpercent)
:mulloop
set _left=%_left%0
set/a mulcnt+=1
set/a _left_tmp=%_left%+1>nul 2>&1||goto one_step_back
if %_left_tmp% GTR 0 (set/a _left_tmp-=1&goto mulloop)
::above piece of code "extends" the dividend until it exceeds 32-bit size, to get the longest decimal expansion of the fraction possible
:one_step_back
set _left=%_left:~0,-1%&set/a mulcnt-=3
set/a percent=(%_left%/%_right%)
set percent=0000000%percent%
if %mulcnt% EQU 0 goto point_shift_out
:point_shift
set percent_tmp=%percent:~-1%%percent_tmp%&set percent=%percent:~0,-1%&set/a mulcnt-=1
if %mulcnt% GTR 0 (goto point_shift)
:point_shift_out
if defined percent_tmp (set percent_tmp=%percent%,%percent_tmp%&set mantissa_present=true) else (set percent_tmp=%percent%)
:trimleadingzeros
if "%percent_tmp:~0,1%"=="0" (if not "%percent_tmp:~0,2%"=="0," (set percent_tmp=%percent_tmp:~1%&goto trimleadingzeros))
if "%mantissa_present%"=="true" (
if "%trimtrailingzeros%"=="yes" (
:trimtrailingzeros
if "%percent_tmp:~-1%"=="0" (set percent_tmp=%percent_tmp:~0,-1%&goto trimtrailingzeros)
)
)
if "%percent_tmp:~-1%"=="," (set percent_tmp=%percent_tmp:~0,-1%)
:spopiwt_setpercent
set percent=%percent_tmp%&set percent_tmp=
if "%updateprogressbar%"=="yes" (if not "%percent%"=="%_prev_percent%" (call:UpdateProgressBar %percent%))
if defined hundred_reached (call:spopiwt_ %2 %2&exit/b0)
:spopiwt_
::feel free to alter the line below
if defined _titlepat (title [%1/%2] [%percent%%%] %_titlepat%) else (title [%1/%2] [%percent%%%])
set exitcode=0
:spopiwt_endproc
exit/b%exitcode%


...and the variant that does not take mantissa into account at all:
@echo off
setlocal EnableDelayedExpansion
prompt $s
echo.
echo Usage:
echo.
echo ShowPercentageOfProgressInWindowTitle ^<stage^> ^<end^> updateprogressbar_[yes^|no]
echo.
echo Example: ShowPercentageOfProgressInWindowTitle 7 11 updateprogressbar_yes
echo Lack of the updateprogressbar_* parameter equals to updateprogressbar_no.
:loop
echo.
set ch_=%ch%
set ch=&set/p ch= give parameters :
if defined ch_ (for /f "tokens=1,2" %%A in ("%ch_%") do (for /f "tokens=1,2" %%C in ("%ch%") do (if "%%A"=="%%C" (if "%%B"=="%%D" (echo.&echo _PREV_PERCENT == PERCENT -^> to reduce performance expensiveness, progress bar&echo state stays halted until the routine is given with a different fraction)))))
::for /l %%G in (0,17034,9998958) do (call:ShowPercentageOfProgressInWindowTitle %%G 9998958 updateprogressbar_yes)
if /i "%ch%"=="demo" (call:demo) else (call:ShowPercentageOfProgressInWindowTitle %ch%)
if "%errorlevel%"=="1" (echo.&echo Too few parameters.)
if "%errorlevel%"=="2" (echo.&echo Integer overflow or divisor ^>1e+7.)
if "%errorlevel%"=="3" (echo.&echo Divide-by-zero attempt detected.)
goto loop
goto:eof
:demo
::counts size of all files in system32 root dir...
for %%A in (%windir%\system32\*.*) do (set/a exact_size+=%%~zA)
::floors bytes
set _exact_size=%exact_size:~0,-3%
:: ...and re-enumerates the directory to gradually add next files' sizes...
for %%A in (%windir%\system32\*.*) do (
set/a var+=%%~zA
:: ...to show the behaviour of the function
call:ShowPercentageOfProgressInWindowTitle !var:~0,-3! %_exact_size% updateprogressbar_yes
)
exit/b0
:UpdateProgressBar
setlocal EnableDelayedExpansion
if not defined _prev_percent (goto upb_)
set/a diff=%percent%-%_prev_percent%
::if there is a difference of 1 in whole percents between next calls of this function, the string-substitution feature is used just to replace ": " with "::", not to redraw the whole bar
if "%diff%"=="1" (if not "%diff%"=="%percent%" (set _titlepat=%_titlepat:: =::%&goto upb_endproc))
:upb_
if not "%1"=="0" (goto upbproc)
set _titlepat=
goto upb_endproc
:upbproc
set _titlepat=[
for /l %%G in (1,1,%1) do (set _titlepat=!_titlepat!:)
set/a _significant=100-%1
if "%_significant%"=="0" (goto titlepat_enclose)
for /l %%G in (1,1,%_significant%) do (set _titlepat=!_titlepat! )
:titlepat_enclose
set _titlepat=%_titlepat%]
:upb_endproc
endlocal&set _titlepat=%_titlepat%&exit/b0

:ShowPercentageOfProgressInWindowTitle
setlocal
set frac=%1 %2
if defined updateprogressbar (
:spopiwt_invoke
call:spopiwt_proc %frac%||goto spopiwt_process_is_over
if defined hundred_reached (goto spopiwt_process_is_over)
::works semi-locally, assists itself with two variables that are set globally, to reduce progress bar drawing routine calls
endlocal&set _prev_percent=%percent%&set _titlepat=%_titlepat%&exit/b%spopiwt_returned%
)
shift
shift
:paramloop
if not defined updateprogressbar (if /i "%1"=="updateprogressbar_yes" (set updateprogressbar=yes) else (set updateprogressbar=no))
shift
if not "%1"=="" goto paramloop
goto spopiwt_invoke
:spopiwt_process_is_over
endlocal&exit/b%spopiwt_returned%
:spopiwt_proc
::receives:
::
:: %1 - dividend (x%)
:: %2 - divisor (100%)
::
::exitcodes:
:: 0 = success calculating and setting everything
:: 1 = too few parameters
:: 2 = denominator out of the threshold
:: 3 = divide-by-zero error
::
set _left=%1
set _right=%2
if defined _left (if defined _right (goto spopiwt_continue))
set exitcode=1
goto spopiwt_endproc
:spopiwt_continue
if not [%_right:~7%]==[] (if not [%_right%]==[10000000] (set exitcode=2&goto spopiwt_endproc))
if %_right% EQU 0 (set exitcode=3&goto spopiwt_endproc)
if %_left% GEQ %_right% (set percent=100&set hundred_reached=true&goto spopiwt_setpercent)
if %_left% EQU 0 (set percent=0&goto spopiwt_setpercent)
:mulloop
set _left=%_left%0
set/a mulcnt+=1
set/a _left_tmp=%_left%+1>nul 2>&1||goto one_step_back
if %_left_tmp% GTR 0 (set/a _left_tmp-=1&goto mulloop)
::above piece of code "extends" the dividend until it exceeds 32-bit size, to get the longest decimal expansion of the fraction possible
:one_step_back
set _left=%_left:~0,-1%&set/a mulcnt-=3
set/a percent=(%_left%/%_right%)
if %mulcnt% EQU 0 goto spopiwt_setpercent
:truncate_expansion
set percent=%percent:~0,-1%&set/a mulcnt-=1
if not defined percent (set percent=0&goto spopiwt_setpercent)
if %mulcnt% GTR 0 (goto truncate_expansion)
:spopiwt_setpercent
if "%updateprogressbar%"=="yes" (if not "%percent%"=="%_prev_percent%" (call:UpdateProgressBar %percent%))
if defined hundred_reached (call:spopiwt_ %2 %2&exit/b0)
:spopiwt_
::feel free to alter the line below
if defined _titlepat (title [%1/%2] [%percent%%%] %_titlepat%) else (title [%1/%2] [%percent%%%])
set exitcode=0
:spopiwt_endproc
exit/b%exitcode%

I have deliberately split these two options for you to choose. The difference in performance is vast, and the effect of using the one that calculates mantissa is not great because the expansion shortens when a fraction has both big dividend and divisor.
As with all the other scripts, I look forward to hearing from you if it works or if it does not work, or if there is a circumstance I had not caught and made the script handle it.
Tested/working flawlessly in Windows 7 and Windows 8.

The former one, alpha version of ShowPercentageOfProgressInWindowTitle, is available for download from my Google Drive. I decided not to list it here anymore, since it is such a behemoth that I do not want anyone to get heart attack. :)

Oct 14, 2013

Function: LargeInt_sub

Subtracts decimal numbers, handles signed input properly (unlike "raw" LargeInt_add, which does not check input at all, and has to receive unsigned integers). As you can see, it involves scripts I had written earlier, hence it seems a bit long.
@echo off
:loop
echo.
echo  give two big integers to be subtracted : 
set/p first=give first number : 
set/p second=give second number : 
echo.
call:LargeInt_sub first second third
if "%errorlevel%"=="0" (echo  ^(%first%^) - ^(%second%^) = ^(%third%^))
if "%errorlevel%"=="1" (echo  Too few parameters.)
if "%errorlevel%"=="2" (echo  Invalid input.)
goto loop
:LargeInt_sub
setlocal
set v0=%% %1 %%
set v0=%v0: =%
call set left=%v0%
set v0=%% %2 %%
set v0=%v0: =%
call set right=%v0%
if defined left (if defined right (goto lrgintsub))
set exitcode=1
goto lrgintsub_endproc
:negate
set var=%% %1 %%
set var=%var: =%
call set var=%var%
if "%var%"=="0" (
 :negate_
 set var=
 exit/b0
) else (
 if not "%var:~0,1%"=="-" (
  set %1=-%var%
 ) else (
  set var=%var:~1%
 )
 goto negate_
)
:lrgintsub
set sides_xchged=false
set left_IsNegative=false
set right_IsNegative=false
call:IsInteger left||endlocal&&exit/b2
call:IsInteger right||endlocal&&exit/b2
if "%left:~0,1%"=="-" (
 set left_IsNegative=true
 set left=%left:~1%
)
if "%right:~0,1%"=="-" (
 set right_IsNegative=true
 set right=%right:~1%
)
call:LargeInt_cmp left right
::exchange left with right if right is "longer" than left for the subtraction to process properly
if "%errorlevel%"=="2" (set right=%left%&set left=%right%&set left_IsNegative=%right_IsNegative%&set right_IsNegative=%left_IsNegative%&set sides_xchged=true)
if "%left_IsNegative%"=="false" (
 if "%right_IsNegative%"=="false" (
  ::left-right
  call:lrgintsub_l dummy dummy outvar
  if "%sides_xchged%"=="true" (call:negate outvar)
 )
)
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="true" (
  ::-left-(-right)=right-left
  call:lrgintsub_l dummy dummy outvar
  if "%sides_xchged%"=="false" (call:negate outvar)
 )
)
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="false" (
  ::-left-right=-(left+right)
  call:LargeInt_add left right outvar
  if "%sides_xchged%"=="false" (call:negate outvar)
 )
)
if "%left_IsNegative%"=="false" (
 if "%right_IsNegative%"=="true" (
  ::left-(-right)=left+right
  call:LargeInt_add left right outvar
  if "%sides_xchged%"=="true" (call:negate outvar)
 )
)
endlocal&set %3=%outvar%&exit/b%errorlevel%
:lrgintsub_l
set buff=
set lastchar_l=
set lastchar_r=
if defined left (set lastchar_l=%left:~-1%)
if defined right (set lastchar_r=%right:~-1%) else (set lastchar_r=0)
if %lastchar_r% GTR %lastchar_l% (call:lrgintsub_withborrow) else (call:lrgintsub_withoutborrow)
if defined left (set left=%left:~0,-1%)
if defined right (set right=%right:~0,-1%)
if not defined left (if not defined right (goto lrgintsub_opsucc))
goto lrgintsub_l
:lrgintsub_opsucc
set exitcode=0
:lrgintsub_endproc
if "%outvar:~0,1%"=="0" (if not "%outvar:~1%"=="" (set outvar=%outvar:~1%&goto lrgintsub_endproc))
endlocal&set %3=%outvar%&exit/b%exitcode%
:handle_borrow
set/a slider+=1
set left_tmp=%left:~-1%%left_tmp%
set left=%left:~0,-1%
if "%left:~-1,1%"=="0" (goto handle_borrow)
set left_tmp=%left_tmp:0=9%
set/a current_pos=%left:~-1,1%-1
set left_tmp=%current_pos%%left_tmp%&set/a slider+=1
set left=%left:~0,-1%
set left=%left%%left_tmp%
set slider=&set left_tmp=&set left_tmp0=&set current_pos=
exit/b0
:lrgintsub_withborrow
call:handle_borrow
set/a lastchar_l+=10
:lrgintsub_withoutborrow
set/a buff=%lastchar_l%-%lastchar_r%
set outvar=%buff%%outvar%
exit/b0
:LargeInt_cmp
::returns
::
:: ERRORLEVEL
:: 0 = left is greater
:: 1 = equal
:: 2 = right is greater
:: 3 = invalid input
::
setlocal
set v0=%% %1 %%
set v0=%v0: =%
call set left=%v0%
set v0=%% %2 %%
set v0=%v0: =%
call set right=%v0%
if not defined left (endlocal&&exit/b3)
if not defined right (endlocal&&exit/b3)
set left_IsNegative=false
set right_IsNegative=false
if "%left:~0,1%"=="-" (set left_IsNegative=true)
if "%right:~0,1%"=="-" (set right_IsNegative=true)
set cond_gtr=GTR
set cond_lss=LSS
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="false" (
  goto right_is_greater
 )
)
if "%left_IsNegative%"=="false" (
 if "%right_IsNegative%"=="true" (
  goto left_is_greater
 )
)
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="true" (
  set cond_gtr=LSS
  set cond_lss=GTR
 )
)
call:strlen %left%
set sizeof_left=%errorlevel%
call:strlen %right%
set sizeof_right=%errorlevel%
if %sizeof_left% %cond_gtr% %sizeof_right% (
 :left_is_greater
 endlocal&exit/b0
)
if %sizeof_left% %cond_lss% %sizeof_right% (
 :right_is_greater
 endlocal&exit/b2
)
:lrgintcmp_l
set left_tmp=%left:~0,1%
set right_tmp=%right:~0,1%
set left=%left:~1%
set right=%right:~1%
if %left_tmp% %cond_gtr% %right_tmp% (goto left_is_greater)
if %right_tmp% %cond_gtr% %left_tmp% (goto right_is_greater)
if defined left (goto lrgintcmp_l)
endlocal&exit/b1
:strlen
setlocal
set param=%1
if not defined param (set length=0&goto strlen_end)
:strlen_0
set param=%param:~0,-1%
set/a length+=1
if defined param (goto strlen_0)
endlocal&exit/b%length%

:IsInteger
call:IsHex %1 10&&exit/b0||exit/b1
:IsHex
setlocal
set var=%% %1 %%
set var=%var: =%
call set var=%var%
if "%2"=="10" (if "%var:~0,1%"=="-" (set var=%var:~1%)) else (if "%var:~0,2%"=="0x" (set var=%var:~2%))
set IsHex=0
set var=%var:0= %
set var=%var:1= %
set var=%var:2= %
set var=%var:3= %
set var=%var:4= %
set var=%var:5= %
set var=%var:6= %
set var=%var:7= %
set var=%var:8= %
set var=%var:9= %
if "%2"=="10" (goto ishex_)
set var=%var:A= %
set var=%var:B= %
set var=%var:C= %
set var=%var:D= %
set var=%var:E= %
set var=%var:F= %
:ishex_
set var=%var: =%
if defined var (set IsHex=1)
endlocal&exit/b%IsHex%

:LargeInt_add
setlocal
set v0=%% %1 %%
set v0=%v0: =%
call set left=%v0%
set v0=%% %2 %%
set v0=%v0: =%
call set right=%v0%
if defined left (if defined right (goto lrgintadd))
set exitcode=1
goto lrgintadd_endproc
:lrgintadd
set buff=
set lastchar_l=
set lastchar_r=
if defined left (set lastchar_l=%left:~-1%)
if defined right (set lastchar_r=%right:~-1%)
if defined carry (set/a buff+=%carry%&set carry=)
if defined lastchar_l (set/a buff+=%lastchar_l%)
if defined lastchar_r (set/a buff+=%lastchar_r%)
if %buff% GTR 9 (
 set outvar=%buff:~-1%%outvar%
 set carry=%buff:~0,1%
) else (
 set outvar=%buff%%outvar%
)
if defined left (set left=%left:~0,-1%)
if defined right (set right=%right:~0,-1%)
if not defined left (if not defined right (set outvar=%carry%%outvar%&goto lrgintadd_opsucc))
goto lrgintadd
:lrgintadd_opsucc
set exitcode=0
:lrgintadd_endproc
endlocal&set %3=%outvar%&exit/b%exitcode%

Oct 13, 2013

Function: OsCheck

A function that will let your scripts recognize Windows version. Works globally, as you can see below. The OS identifier is kept in osid variable, so you needn't call it many times. Two another variables are flushed just after calling OsCheck. I have nothing more to say about this trivial code.
:: works globally, uses vars osid, os_str, os_str_  
@echo off
echo.
call:OsCheck
for %%A in (os_str os_str_) do (if defined %%A (set %%A=))
if "%osid%"=="0" (echo  Unknown OS, probably older than Windows 2000.)
if "%osid%"=="1" (echo  Windows 2000)
if "%osid%"=="2" (echo  Windows XP)
if "%osid%"=="3" (echo  Windows Server 2003)
if "%osid%"=="4" (echo  Windows Vista)
if "%osid%"=="5" (echo  Windows Server 2008)
if "%osid%"=="6" (echo  Windows 7)
if "%osid%"=="7" (echo  Windows 8)
if "%osid%"=="8" (echo  Unknown OS, probably newer than Windows 8.)
echo.
pause
goto:eof
:OsCheck
if not exist "%windir%\system32\reg.exe" (for /f "tokens=3" %%A in ('ver') do (if "%%A"=="2000" (set osid=1&exit/b0) else (set osid=0&exit/b0)))
for /f "tokens=3*" %%G in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v "ProductName"') do (set os_str=%%G %%H)
set os_str_=%os_str:Windows 7=%
if not "%os_str%"=="%os_str_%" (set osid=6&exit/b0)
set os_str_=%os_str:Windows 8=%
if not "%os_str%"=="%os_str_%" (set osid=7&exit/b0)
set os_str_=%os_str:XP=%
if not "%os_str%"=="%os_str_%" (set osid=2&exit/b0)
set os_str_=%os_str:Vista=%
if not "%os_str%"=="%os_str_%" (set osid=4&exit/b0)
set os_str_=%os_str:2003=%
if not "%os_str%"=="%os_str_%" (set osid=3&exit/b0)
set os_str_=%os_str:2008=%
if not "%os_str%"=="%os_str_%" (set osid=5&exit/b0)
set os_str_=%os_str:2000=%
if not "%os_str%"=="%os_str_%" (set osid=1&exit/b0)
set osid=8
exit/b0

Oct 12, 2013

Function: LargeInt_cmp

Compares decimal numbers.
@echo off
:loop
set/p ch= give 2 numbers one after another : 
for /f "tokens=1,2" %%G in ("%ch%") do (set left=%%G&set right=%%H)
call:LargeInt_cmp left right
if "%errorlevel%"=="0" (echo  Left is greater.)
if "%errorlevel%"=="1" (echo  Equal to each other.)
if "%errorlevel%"=="2" (echo  Right is greater.)
if "%errorlevel%"=="3" (echo  Invalid input.)
echo.
goto loop
:LargeInt_cmp
::returns
::
:: ERRORLEVEL
:: 0 = left is greater
:: 1 = equal
:: 2 = right is greater
:: 3 = invalid input
::
setlocal
set v0=%% %1 %%
set v0=%v0: =%
call set left=%v0%
set v0=%% %2 %%
set v0=%v0: =%
call set right=%v0%
if not defined left (endlocal&&exit/b3)
if not defined right (endlocal&&exit/b3)
call:IsInteger left||endlocal&&exit/b3
call:IsInteger right||endlocal&&exit/b3
set left_IsNegative=false
set right_IsNegative=false
if "%left:~0,1%"=="-" (set left_IsNegative=true)
if "%right:~0,1%"=="-" (set right_IsNegative=true)
set cond_gtr=GTR
set cond_lss=LSS
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="false" (
  goto right_is_greater
 )
)
if "%left_IsNegative%"=="false" (
 if "%right_IsNegative%"=="true" (
  goto left_is_greater
 )
)
if "%left_IsNegative%"=="true" (
 if "%right_IsNegative%"=="true" (
  set cond_gtr=LSS
  set cond_lss=GTR
 )
)
call:strlen %left%
set sizeof_left=%errorlevel%
call:strlen %right%
set sizeof_right=%errorlevel%
if %sizeof_left% %cond_gtr% %sizeof_right% (
 :left_is_greater
 endlocal&exit/b0
)
if %sizeof_left% %cond_lss% %sizeof_right% (
 :right_is_greater
 endlocal&exit/b2
)
:wigloop
set left_tmp=%left:~0,1%
set right_tmp=%right:~0,1%
set left=%left:~1%
set right=%right:~1%
if %left_tmp% %cond_gtr% %right_tmp% (goto left_is_greater)
if %right_tmp% %cond_gtr% %left_tmp% (goto right_is_greater)
if defined left (goto wigloop)
endlocal&exit/b1

:strlen
setlocal
set param=%1
if not defined param (set length=0&goto strlen_end)
:strlen_0
set param=%param:~0,-1%
set/a length+=1
if defined param (goto strlen_0)
endlocal&exit/b%length%

:IsInteger
call:IsHex %1 10&&exit/b0||exit/b1
:IsHex
setlocal
set var=%% %1 %%
set var=%var: =%
call set var=%var%
if "%2"=="10" (if "%var:~0,1%"=="-" (set var=%var:~1%)) else (if "%var:~0,2%"=="0x" (set var=%var:~2%))
set IsHex=0
set var=%var:0= %
set var=%var:1= %
set var=%var:2= %
set var=%var:3= %
set var=%var:4= %
set var=%var:5= %
set var=%var:6= %
set var=%var:7= %
set var=%var:8= %
set var=%var:9= %
if "%2"=="10" (goto ishex_)
set var=%var:A= %
set var=%var:B= %
set var=%var:C= %
set var=%var:D= %
set var=%var:E= %
set var=%var:F= %
:ishex_
set var=%var: =%
if defined var (set IsHex=1)
endlocal&exit/b%IsHex%

Oct 10, 2013

Function: Hex2Dec

Converts hexadecimal values to decimal. Supports numbers larger than 264 (0xFFFFFFFF'FFFFFFFF). This script utilizes pre-generated multiplication table. It does exactly the same what humans do to convert hexadecimal to decimal by hand. It grabs proper decimal values from the table (array) and saves it to a variable that I like to name add chain. As the assignment finishes, the script sums all the numbers from such an add chain.
Seven powers of 16 are summed internally, using the set /a command. Why not eight? Take a sheet of paper and find out (hint: cmd.exe supports 32-bit signed integers from the range of -231 ... 231-1). These 7 numbers are summed the way they become one number in the add chain. If a number is greater than 264, then the script calculates further multiples of 16, till "the end".
The latest stage of conversion is summing of values from the add chain by the LargeInt_add procedure.
I can not guarantee the following script is 100% correct, neither did I check what's the biggest number it could handle. The multiple table has been generated using... LargeInt_add.
@echo off
:h2d
echo.
set/p var=give hex value 
call:HexToDec var var0
echo.
echo 0x%var% --^> %var0%
goto h2d
:HexToDec
setlocal&set var=%% %1 %%&set var=%var: =%&call set var=%var%
set sum=0
set exp=-1
set base=16
set pow16_0_=0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
set pow16_1_=16;256;4096;65536;1048576;16777216;268435456;4294967296;68719476736;1099511627776;17592186044416;281474976710656;4503599627370496;72057594037927936;1152921504606846976
set pow16_2_=32;512;8192;131072;2097152;33554432;536870912;8589934592;137438953472;2199023255552;35184372088832;562949953421312;9007199254740992;144115188075855872;2305843009213693952
set pow16_3_=48;768;12288;196608;3145728;50331648;805306368;12884901888;206158430208;3298534883328;52776558133248;844424930131968;13510798882111488;216172782113783808;3458764513820540928
set pow16_4_=64;1024;16384;262144;4194304;67108864;1073741824;17179869184;274877906944;4398046511104;70368744177664;1125899906842624;18014398509481984;288230376151711744;4611686018427387904
set pow16_5_=80;1280;20480;327680;5242880;83886080;1342177280;21474836480;343597383680;5497558138880;87960930222080;1407374883553280;22517998136852480;360287970189639680;5764607523034234880
set pow16_6_=96;1536;24576;393216;6291456;100663296;1610612736;25769803776;412316860416;6597069766656;105553116266496;1688849860263936;27021597764222976;432345564227567616;6917529027641081856
set pow16_7_=112;1792;28672;458752;7340032;117440512;1879048192;30064771072;481036337152;7696581394432;123145302310912;1970324836974592;31525197391593472;504403158265495552;8070450532247928832
set pow16_8_=128;2048;32768;524288;8388608;134217728;2147483648;34359738368;549755813888;8796093022208;140737488355328;2251799813685248;36028797018963968;576460752303423488;9223372036854775808
set pow16_9_=144;2304;36864;589824;9437184;150994944;2415919104;38654705664;618475290624;9895604649984;158329674399744;2533274790395904;40532396646334464;648518346341351424;10376293541461622784
set pow16_10_=160;2560;40960;655360;10485760;167772160;2684354560;42949672960;687194767360;10995116277760;175921860444160;2814749767106560;45035996273704960;720575940379279360;11529215046068469760
set pow16_11_=176;2816;45056;720896;11534336;184549376;2952790016;47244640256;755914244096;12094627905536;193514046488576;3096224743817216;49539595901075456;792633534417207296;12682136550675316736
set pow16_12_=192;3072;49152;786432;12582912;201326592;3221225472;51539607552;824633720832;13194139533312;211106232532992;3377699720527872;54043195528445952;864691128455135232;13835058055282163712
set pow16_13_=208;3328;53248;851968;13631488;218103808;3489660928;55834574848;893353197568;14293651161088;228698418577408;3659174697238528;58546795155816448;936748722493063168;14987979559889010688
set pow16_14_=224;3584;57344;917504;14680064;234881024;3758096384;60129542144;962072674304;15393162788864;246290604621824;3940649673949184;63050394783186944;1008806316530991104;16140901064495857664
set pow16_15_=240;3840;61440;983040;15728640;251658240;4026531840;64424509440;1030792151040;16492674416640;263882790666240;4222124650659840;67553994410557440;1080863910568919040;17293822569102704640
:loop_
if not defined _var (
 set _var=%var:~-1%
) else (
 set _var=%_var%;%var:~-1%
)
set var=%var:~0,-1%
if defined var (goto loop_)
for %%A in (%_var%) do (call:loop %%A)
set n_=0
for %%A in (%chain%) do (set/a n_+=1)
if %n_% EQU 0 (goto:hextodec_out)
if %n_% LEQ 7 (call:sum_lesser&for /f "tokens=2* delims=^=" %%G in ('set component_') do (set sum=%%G)) else (call:sum_bigger)
:hextodec_out
endlocal&set %2=%sum%&exit/b0
:sum_bigger
call:sum_lesser
set chain=%component_%;%chain%
:sum_bigger_
for /f "tokens=1,2* delims=;" %%A in ("%chain%") do (set component=%%A&if "%%C" =="" (set chain=%%B) else (set chain=%%B;%%C))
if not "%component:~0,1%"=="0" (call:LargeInt_add sum component sum)
if defined chain (goto sum_bigger_)
exit/b0
:sum_lesser
for /f "tokens=1,2,3,4,5,6,7 delims=;" %%A in ("%chain%") do (call:sum_smaller_components %%A %%B %%C %%D %%E %%F %%G)
exit/b0
:sum_smaller_components
set/a component_+=%1
for /f "tokens=2* delims=;" %%H in ("%chain%") do (set chain=%%H;%%I)
shift
if not "%1"=="" (goto sum_smaller_components)
exit/b0
:loop
set num_=%1
if /i "%num_%"=="A" (set num_=10)
if /i "%num_%"=="B" (set num_=11)
if /i "%num_%"=="C" (set num_=12)
if /i "%num_%"=="D" (set num_=13)
if /i "%num_%"=="E" (set num_=14)
if /i "%num_%"=="F" (set num_=15)
set/a exp+=1
if %exp% GTR 0 (
 if %exp% LSS 16 (
  for /f "tokens=2* delims=^=" %%A in ('set pow16_%num_%_') do (for /f "tokens=%exp% delims=;" %%C in ("%%A;%%B") do (set num=%%C))
 )
 if %exp% GTR 15 (
  call:further_multiplies
 )
) else (set num=%num_%)
if defined chain (
 set chain=%chain%;%num%
) else (set chain=%num%)
exit/b0

:further_multiplies
if not defined tokens (set tokens=15)
for /f "tokens=2* delims=^=" %%G in ('set pow16_1_') do (for /f "tokens=%tokens% delims=;" %%H in ("%%G") do set pow16=%%H)
call:LargeInt_mul pow16 base pow16
set pow16_1_=%pow16_1_%;%pow16%
set/a tokens+=1
if "%num_%"=="0" (set num=0) else (call:LargeInt_mul num_ pow16 num)
exit/b0


:handle_buff
if %buff% GTR 9 (set buff=%buff:~-1%&set carry=%buff:~0,1%) else (set carry=)
exit/b0
:LargeInt_mul
setlocal
set left=%% %1 %%
set left=%left: =%
call set left=%left%
set right=%% %2 %%
set right=%right: =%
call set right=%right%
if defined left (if defined right (goto lrgintmul))
set exitcode=1
goto lrgintmul_endproc
:lrgintmul
set upper_component=%left%
set lower_component=%right%
:lrgintmul_loop
set temp_component_l=
set temp_component_u=
set temp_component_l=%lower_component:~-1%
set temp_component_u=%upper_component:~-1%
if defined temp_component_l (
 if defined temp_component_u (
  set/a buff=%temp_component_l%*%temp_component_u%
  if defined carry (set/a buff+=%carry%)
  call:handle_buff
 )
)
if "%suffix_added%"=="false" (
 set subst=%buff%%subst%%suffix%
) else (
 set subst=%buff%%subst%
)
set suffix_added=true
set upper_component=%upper_component:~0,-1%
if not defined upper_component (
 if defined carry (
  set subst=%carry%%subst%&set carry=
 )
)
if not defined upper_component (
 if not defined lower_component (
  goto lrgintmul_opsucc
 )
 set add_chain=%add_chain%;%subst%
 set subst=
 set upper_component=%left%
 set lower_component=%lower_component:~0,-1%
 set suffix_added=false
 set suffix=%suffix%0
)
if defined lower_component (goto lrgintmul_loop)
set sum=0
for %%A in (%add_chain%) do (set component=%%A&call:LargeInt_add sum component sum)
:lrgintmul_opsucc
set exitcode=0
:lrgintmul_endproc
call:ThrowErrorLevel exitcode %exitcode%
endlocal&set %3=%sum%&exit/b%errorlevel%


:LargeInt_add
setlocal
set left=%% %1 %%
set left=%left: =%
call set left=%left%
set right=%% %2 %%
set right=%right: =%
call set right=%right%
if defined left (if defined right (goto lrgintadd))
set exitcode=1
goto lrgintadd_endproc
:lrgintadd
set buff=
set lastchar_l=
set lastchar_r=
if defined left (set lastchar_l=%left:~-1%)
if defined right (set lastchar_r=%right:~-1%)
if defined carry (set/a buff+=%carry%&set carry=)
if defined lastchar_l (set/a buff+=%lastchar_l%)
if defined lastchar_r (set/a buff+=%lastchar_r%)
if %buff% GTR 9 (
 set outvar=%buff:~-1%%outvar%
 set carry=%buff:~0,1%
) else (
 set outvar=%buff%%outvar%
)
if defined left (set left=%left:~0,-1%)
if defined right (set right=%right:~0,-1%)
if not defined left (if not defined right (set outvar=%carry%%outvar%&goto lrgintadd_opsucc))
goto lrgintadd
:lrgintadd_opsucc
set exitcode=0
:lrgintadd_endproc
call:ThrowErrorLevel exitcode %exitcode%
endlocal&set %3=%outvar%&exit/b%errorlevel%
:ThrowErrorLevel
if defined %1 set %1=
exit/b%2

Oct 9, 2013

Batch code variants: EnumerateProfileSids

No, it is not a verbatim re-post of my older work. GetCurrentSID and EnumerateLoggedOnSIDs are functions that utilize a bit different method from the one I am about to describe. What they all do is guessing, accurate guessing. This function enumerates user profiles' SIDs with lesser uncertainty factor than the former. EnumerateProfileSids retrieves SIDs from the ProfileList registry key. I have worked out two variants, and chose the faster (because both are good and give the same results) one.

First, I am going to present the slower variant. What does it do? It retrieves Sid values from ProfileList subkeys, explodes long hex values properly so that they can later be converted to decimal, thus to concatenate the decimal values into a valid SID.
This slow script also demonstrates a basic cache. When a newly encountered DWORD is converted, it is saved to a variable which is an array. When it encounters a DWORD that had already been converted, it gets the previously converted decimal form of the matching DWORD from that array. This cache takes only three biggest "parts" of an SID into account, because conversion of the latest DWORD (Relative ID) is very fast, hence it is not necessary for it to be saved.
@echo off
call:EnumerateProfileSids var
for %%A in (%var%) do (
 for /f "tokens=1,2,3,4 delims=+" %%B in (%%A) do (
  echo.
  echo  SID                  : %%B
  echo  Logged in            : %%C
  echo  Possible username    : %%D
  echo  Profile storage path : %%E
 )
)
pause
goto:eof
:EnumerateProfileSids
setlocal
set key=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
set pow16_0_=0;0;0;0;0;0;0;0&set pow16_1_=16;256;4096;65536;1048576;16777216;268435456;4294967296
set pow16_2_=32;512;8192;131072;2097152;33554432;536870912;8589934592&set pow16_3_=48;768;12288;196608;3145728;50331648;805306368;12884901888
set pow16_4_=64;1024;16384;262144;4194304;67108864;1073741824;17179869184&set pow16_5_=80;1280;20480;327680;5242880;83886080;1342177280;21474836480
set pow16_6_=96;1536;24576;393216;6291456;100663296;1610612736;25769803776&set pow16_7_=112;1792;28672;458752;7340032;117440512;1879048192;30064771072
set pow16_8_=128;2048;32768;524288;8388608;134217728;2147483648;34359738368&set pow16_9_=144;2304;36864;589824;9437184;150994944;2415919104;38654705664
set pow16_10_=160;2560;40960;655360;10485760;167772160;2684354560;42949672960&set pow16_11_=176;2816;45056;720896;11534336;184549376;2952790016;47244640256
set pow16_12_=192;3072;49152;786432;12582912;201326592;3221225472;51539607552&set pow16_13_=208;3328;53248;851968;13631488;218103808;3489660928;55834574848
set pow16_14_=224;3584;57344;917504;14680064;234881024;3758096384;60129542144&set pow16_15_=240;3840;61440;983040;15728640;251658240;4026531840;64424509440
for /f "tokens=7 delims=\" %%A in ('reg query "%key%"') do (for /f "tokens=1,2,3,4,5,6,7,8,9 delims=.-" %%B in ("%%A") do (if not "%%B"=="" (if not "%%C"=="" (if not "%%D"=="" (if not "%%E"=="" (if not "%%F"=="" (if not "%%G"=="" (if not "%%H"=="" (if not "%%I"=="" (if "%%J"=="" (for /f "tokens=3 skip=2" %%K in ('reg query "%key%\%%A" /v "Sid"') do (call:process_sid %%K))))))))))))
endlocal&set %1=%sids%&exit/b0
:process_sid
set sid=%1
set sid_0=%sid:~-42,2%&set sid_1=%sid:~-40,2%&set sid_2=%sid:~-26,2%%sid:~-28,2%%sid:~-30,2%%sid:~-32,2%&set sid_3=%sid:~-18,2%%sid:~-20,2%%sid:~-22,2%%sid:~-24,2%&set sid_4=%sid:~-10,2%%sid:~-12,2%%sid:~-14,2%%sid:~-16,2%&set sid_5=%sid:~-2,2%%sid:~-4,2%%sid:~-6,2%%sid:~-8,2%
:sid5
if "%sid_5:~0,1%"=="0" (set sid_5=%sid_5:~1%&goto sid5)
set/a iter+=1
set/a sid_0=0x%sid_0%*1
set/a sid_1=0x%sid_1%*1
set sid_2_0=%sid_2%
set sid_3_0=%sid_3%
set sid_4_0=%sid_4%
if defined tokenchain (call:checkifalreadycvted)
if "%sid_2_0%"=="%sid_2%" (call:sid16_10 %sid_2% sid_2)
if "%sid_3_0%"=="%sid_3%" (call:sid16_10 %sid_3% sid_3)
if "%sid_4_0%"=="%sid_4%" (call:sid16_10 %sid_4% sid_4)
if defined tokenchain (
 call:addtoken2ifnotinthetokenlist&call:addtoken3ifnotinthetokenlist&call:addtoken4ifnotinthetokenlist
) else (set tokenchain=%sid_2_0%+%sid_2%;%sid_3_0%+%sid_3%;%sid_4_0%+%sid_4%)
set/a sid_5=0x%sid_5%*1
set sid=S-1-%sid_0%-%sid_1%-%sid_2%-%sid_3%-%sid_4%-%sid_5%
for /f "tokens=3*" %%A in ('reg query "%key%\%sid%" /v "ProfileImagePath"') do (if not "%%B"=="" (set ProfileImagePath=%%A %%B) else (set ProfileImagePath=%%A))
set LoggedIn=False
set reported_username=
reg query "HKU\%sid%\Volatile Environment">nul 2>&1&&set LoggedIn=True&&reg query "HKU\%sid%\Volatile Environment" /v "USERNAME">nul 2>&1&&for /f "tokens=3*" %%A in ('reg query "HKU\%sid%\Volatile Environment" /v "USERNAME"') do (if not "%%B"=="" (set reported_username=%%A %%B) else (set reported_username=%%A))
if "%ProfileImagePath:~-1,1%"=="\" (set ProfileImagePath=%ProfileImagePath:~0,-1%)
if not defined reported_username (call:extract_the_deepest_dir_from_path ProfileImagePath reported_username)
if not defined sids (
 set sids="%sid%+%LoggedIn%+%reported_username%+%ProfileImagePath%"
) else (
 set sids=%sids%;"%sid%+%LoggedIn%+%reported_username%+%ProfileImagePath%"
)
exit/b0
:extract_the_deepest_dir_from_path
::syntax:  call:extract_the_deepest_dir_from_path path_var_name last_token_name
setlocal&set v1=&set v2=&set v0=%%%1%%&for /f "tokens=2 delims=^=" %%A in ('set v0') do (call set v0=%%A)
:etddfp_
set v1=[%v0:~-1,1%]
if not "%v1%"=="[\]" (set v2=%v1:~1,1%%v2%&set v0=%v0:~0,-1%&goto etddfp_) else (endlocal&set %2=%v2%&exit/b0)
:addtoken2ifnotinthetokenlist
for %%A in (%tokenchain%) do (for /f "tokens=1 delims=+" %%B in ("%%A") do (if "%%B"=="%sid_2_0%" (exit/b0)))
set tokenchain=%tokenchain%;%sid_2_0%+%sid_2%&exit/b0
:addtoken3ifnotinthetokenlist
for %%A in (%tokenchain%) do (for /f "tokens=1 delims=+" %%B in ("%%A") do (if "%%B"=="%sid_3_0%" (exit/b0)))
set tokenchain=%tokenchain%;%sid_3_0%+%sid_3%&exit/b0
:addtoken4ifnotinthetokenlist
for %%A in (%tokenchain%) do (for /f "tokens=1 delims=+" %%B in ("%%A") do (if "%%B"=="%sid_4_0%" (exit/b0)))
set tokenchain=%tokenchain%;%sid_4_0%+%sid_4%&exit/b0
:checkifalreadycvted
for /l %%A in (2,1,4) do (for /f "tokens=2 delims=^=" %%B in ('set sid_%%A_0') do (for %%C in (%tokenchain%) do (for /f "tokens=1,2 delims=+" %%D in ("%%C") do (if "%%D"=="%%B" (set sid_%%A=%%E)))))
exit/b0
:sid16_10
setlocal
set var=%1
set sum=0
set exp=-1
set base=16
:loop_
if not defined _var (
 set _var=%var:~-1%
) else (
 set _var=%_var%;%var:~-1%
)
set var=%var:~0,-1%
if defined var (goto loop_)
for %%A in (%_var%) do (call:loop %%A)
set n_=0
for %%A in (%chain%) do (set/a n_+=1)
if %n_% EQU 0 (goto:hextodec_out)
if %n_% LEQ 7 (call:sum_lesser&for /f "tokens=2* delims=^=" %%G in ('set component_') do (set sum=%%G)) else (call:sum_bigger)
:hextodec_out
endlocal&set %2=%sum%&exit/b0
:sum_bigger
call:sum_lesser
set chain=%component_%;%chain%
:sum_bigger_
for /f "tokens=1,2* delims=;" %%A in ("%chain%") do (set component=%%A&if "%%C" =="" (set chain=%%B) else (set chain=%%B;%%C))
if not "%component:~0,1%"=="0" (call:LargeInt_add sum component sum)
if defined chain (goto sum_bigger_)
exit/b0
:sum_lesser
for /f "tokens=1,2,3,4,5,6,7 delims=;" %%A in ("%chain%") do (call:sum_smaller_components %%A %%B %%C %%D %%E %%F %%G)
exit/b0
:sum_smaller_components
set/a component_+=%1
for /f "tokens=2* delims=;" %%H in ("%chain%") do (set chain=%%H;%%I)
shift
if not "%1"=="" (goto sum_smaller_components)
exit/b0
:loop
set num_=%1
if /i "%num_%"=="A" (set num_=10)
if /i "%num_%"=="B" (set num_=11)
if /i "%num_%"=="C" (set num_=12)
if /i "%num_%"=="D" (set num_=13)
if /i "%num_%"=="E" (set num_=14)
if /i "%num_%"=="F" (set num_=15)
set/a exp+=1
if %exp% GTR 0 (
 for /f "tokens=2* delims=^=" %%A in ('set pow16_%num_%_') do (for /f "tokens=%exp% delims=;" %%C in ("%%A;%%B") do (set num=%%C))
) else (set num=%num_%)
if defined chain (
 set chain=%chain%;%num%
) else (set chain=%num%)
exit/b0

:LargeInt_add
setlocal
set left=%% %1 %%
set left=%left: =%
call set left=%left%
set right=%% %2 %%
set right=%right: =%
call set right=%right%
if defined left (if defined right (goto lrgintadd))
set exitcode=1
goto lrgintadd_endproc
:lrgintadd
set buff=
set lastchar_l=
set lastchar_r=
if defined left (set lastchar_l=%left:~-1%)
if defined right (set lastchar_r=%right:~-1%)
if defined carry (set/a buff+=%carry%&set carry=)
if defined lastchar_l (set/a buff+=%lastchar_l%)
if defined lastchar_r (set/a buff+=%lastchar_r%)
if %buff% GTR 9 (
 set outvar=%buff:~-1%%outvar%
 set carry=%buff:~0,1%
) else (
 set outvar=%buff%%outvar%
)
if defined left (set left=%left:~0,-1%)
if defined right (set right=%right:~0,-1%)
if not defined left (if not defined right (set outvar=%carry%%outvar%&goto lrgintadd_opsucc))
goto lrgintadd
:lrgintadd_opsucc
set exitcode=0
:lrgintadd_endproc
call:ThrowErrorLevel exitcode %exitcode%
endlocal&set %3=%outvar%&exit/b%errorlevel%
:ThrowErrorLevel
if defined %1 set %1=
exit/b%2
The above variant is just a theoretical example, as it is terribly slow: it needs over 3 times (333%) the time the next one. The aim of writing it was to show that it is possible to handle hex values, plus properly converting them to decimal (this script can cope with DWORDs decimal value of which exceeds 2^31-1). It is slow, but it works, and is possible.
This one also gets data from the ProfileList key, but acts more lazily. It just gets user name from ProfileImagePath value of each existing key of the pattern of a "typical" SID, and takes the subkey name, which is believed to be always equal with what's in Sid value in that subkey.

My objective opinion on why did Microsoft decide to store SID in one place in two forms
I think it is that it could be faster for the OS to grab "raw" SID (in hexadecimal form) from a REG_BINARY value rather than to convert string SID (included in the key name) to hexadecimal form each time it's needed.

Here is the faster one.
@echo off
call:EnumerateProfileSids var
for %%A in (%var%) do (
 for /f "tokens=1,2,3,4 delims=+" %%B in (%%A) do (
  echo.
  echo  SID                  : %%B
  echo  Logged in            : %%C
  echo  Possible username    : %%D
  echo  Profile storage path : %%E
 )
)
pause
goto:eof
:EnumerateProfileSids
setlocal
set key=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
for /f "tokens=7 delims=\" %%A in ('reg query "%key%"') do (for /f "tokens=1,2,3,4,5,6,7,8,9 delims=.-" %%B in ("%%A") do (if not "%%B"=="" (if not "%%C"=="" (if not "%%D"=="" (if not "%%E"=="" (if not "%%F"=="" (if not "%%G"=="" (if not "%%H"=="" (if not "%%I"=="" (if "%%J"=="" (call:process_sid %%B-%%C-%%D-%%E-%%F-%%G-%%H-%%I)))))))))))
endlocal&set %1=%sids%&exit/b0
:process_sid
for /f "tokens=3*" %%A in ('reg query "%key%\%1" /v "ProfileImagePath"') do (if not "%%B"=="" (set ProfileImagePath=%%A %%B) else (set ProfileImagePath=%%A))
set LoggedIn=False
set reported_username=
reg query "HKU\%1\Volatile Environment">nul 2>&1&&set LoggedIn=True&&reg query "HKU\%1\Volatile Environment" /v "USERNAME">nul 2>&1&&for /f "tokens=3*" %%A in ('reg query "HKU\%1\Volatile Environment" /v "USERNAME"') do (if not "%%B"=="" (set reported_username=%%A %%B) else (set reported_username=%%A))
if "%ProfileImagePath:~-1,1%"=="\" (set ProfileImagePath=%ProfileImagePath:~0,-1%)
if not defined reported_username (call:extract_the_deepest_dir_from_path ProfileImagePath reported_username)
if not defined sids (
 set sids="%1+%LoggedIn%+%reported_username%+%ProfileImagePath%"
) else (
 set sids=%sids%;"%1+%LoggedIn%+%reported_username%+%ProfileImagePath%"
)
exit/b0
:extract_the_deepest_dir_from_path
::syntax:  call:extract_the_deepest_dir_from_path path_var_name last_token_name
setlocal&set v1=&set v2=&set v0=%%%1%%&for /f "tokens=2 delims=^=" %%A in ('set v0') do (call set v0=%%A)
:etddfp_
set v1=[%v0:~-1,1%]
if not "%v1%"=="[\]" (set v2=%v1:~1,1%%v2%&set v0=%v0:~0,-1%&goto etddfp_) else (endlocal&set %2=%v2%&exit/b0)


Rarely do I recommend others' work, but in this case that page assured me about my guesses.
A bit off-topic: I overtook the future a bit, because the slower variant of the function I described uses Hex2Dec procedure I am going to release. I have it ready, and I will publish it soon so please hold on from using sid16_10 because it is truncated for needs of the script. The original ("full") one supports numbers greater than 2^64.