Tag Archives: Shell Script

npm install webdriver is not working properly: 3 solutions

So, you thought about using locally installed Microsoft Edge (chromium) or Google Chrome as automaton for your nodeJS project? Bad luck, the webdriver versions available on npm for Windows do not match the browser’s MAJOR version… That’s a FAIL! Here is how to circumvent this problem.

Continue reading npm install webdriver is not working properly: 3 solutions

How To Add missing Retroarch Snapshots for MAME ?

Retroarch provides Thumbnails for games that you have, but the list is curated and limited. MAME full non-merged has 44,000+ games and Retroarch provides only 13,000 or so. This guide will show you how to add almost all of the 44,000+ Retroarch Snapshots missing, thanks to progetto.

Continue reading How To Add missing Retroarch Snapshots for MAME ?

Libretro 101: Getting Started with Retroarch Windows PC

Retroarch can be confusing, and is not working well out-of-the-box. This guide will help you getting started fresh in minutes. It compiles all the information I wish I had when I started months ago.

 

Presentation of Retroarch

Retroarch is a frontend for libretro

Retroarch is NOT an emulator.

Retroarch is a cross-platform, sophisticated frontend for the libretro API. Licensed GPLv3.

Libretro is a meta-project hosting tons of emulator cores. Each core is an emulator platform. In the past, we had individual emulators for each platform, in the form of a classic app with executable, DLL, assets folders, etc. Most have been ported to libretro as a single file. On windows, they present themselves as a single DLL:

retroarch-cores-dll

Other frontends currently maintained as of 2022 and based off Libretro (or compatible with):

  1. Retroarch (Windows, PS3, Wii, Linux, etc)
  2. Batocera (Linux pendrive)
  3. BizHawk (Windows)
  4. OpenEmu (MacOS)
  5. RetroBat aka Emulation Station (Windows)

 

Retroarch is trending

A simple google trends lookup later, shows Retroarch as a clear winner:

Retroarch-google-trend-2022-04

Surprisingly, Linux solution such as OpenEmu do not seem to take off. Let’s get back to it on another thread.

 

Rant against Retrobat

I do not recommend Retrobat for the following reasons:

  • Retrobat is developed by a French oligarchy who cannot hear critiques. They assume things about you, before trying to help.
  • Retrobat is based on outdated dotNET 2.0 and 3.x – which is impossible to install on modern machines, without some tweaks and hacked binaries.
  • Retrobat does not work out of the box. It comes packaged with some Libretro cores, but don’t tell you which versions they are. I tried games that work on Retroarch with their version of FBneo, NES, SMNES, Genesis, nothing works.
  • Asked for help on their discord and got this: “Oh, if your machine cannot run Retroarch, forget about Retrobat”. Nice. Did I ever say I cannot run Retroarch?

As French know-it-all geniuses, these people assume things before asking and categorize you immediately. If it works for you, great. I’ll pass.

 

Install Retroarch portable

Download and Unpack Retroarch and Cores

The best way to maintain it, is to run it in portable mode. Get the latest stable release from here: 1.10.2 as of April 2022

  • Retroarch itself
  • The cores as well

Retroarch

Both will unzip under RetroArch-Win64 – keep it as it, it’s easier to maintain. Later on, you can update the installation by downloading the latest 20XX-XX-XX-RetroArch.7z package and overwriting the executable.

You can also let it download each core from within the frontend, but why bother when you have a convenient zipfile available?

 

Download Assets

You can but you should let Retroarch download them from the fronted. This guarantees the compatibility for the version you have.

Retroarch

 

Download BIOSpacks

It’s not necessary if your collection of Arcade games is full non-merged or merged. It does not hurt to have it either way.

BIOS files go into Retroarch-x64\system\– unzip it under that folder.

 

Setup RetroArch

On the first run you will be greeted by the ozone menu:

Retroarch

You can change this in the Drivers screen:

Retroarch

  • glui: smartphone menu

Retroarch

  • rgui: Wii menu

Retroarch

  • xmb: Xbox menu

Retroarch

 

Setup Drivers

Retroarch

Setup everything exactly like that:

Retroarch

  • Video: the default video driver is vulkan but everyone fond on PSX/Wii/N64 recommend glcore
  • Audio: some recommend wasapi if your audio stutters or fails to play properly, it’s up to you

 

Setup Video

Setup everything like that:

Retroarch

  • Threaded Video: OFF – this will improve CPU demanding games at the cost of accuracy
  • Bilinear Filtering: ON – this is crucial for LED display, without it your games will look clean but aliased and horrible in the end

 

retroarch-bilinear-filtering
comparison between bilinear filtering ON and OFF

As you can see, Bilinear filtering will provide the look and feel of CRT TV on your latest 4K 70″ OLED TV.

There are tons of other filters available but this one is the fastest and bestest off the batch.

 

Output

Everything in there is auto-detected and depends on the video driver you already selected before (glcore or vulkan)

Fullscreen mode

You can enter Fullscreen with ALT + ENTER – no need to tweak this, it will constantly switch your dekstop when you unload games anyway

Windowed Mode

Remember Window Position and Size: ON

Scalling

Retroarch

  • Integer scale: ON – games won’t fit the entire screen but that’s fine, you likely have a 70″ TV anyway and it’s a HUGE boost in performance
  • Integer Scale Overscale: OFF – you don’t want your games to be cropped
  • Aspect Ratio: core provided – let the genius developers of the cores handle that for you
  • Crop Overscan: ON – don’t show garbage pixels on the edges

Synchronization

Retroarch

  • Vsync: OFF – I can’t think of any game in the past, present or future that would benefit of this. It just slows everything down
  • Hard GPU Sync: OFF – even worst than VSync – they did it
  • Sync to Exact Content Framerate: depends on your monitor/TV, and only for fullscreen non-windowed mode

 

Setup Audio

Retroarch

Since we selected xaudio, that should work with Windows just fine. leave everything default over there

 

Input

This is where the fun begins. This is Windows, remember. Every time you plug controllers off and on, you will lose something or something will break.

When plugin in your controllers, try to always plug the same ones in the same order on your USB hub.

Retroarch

After plugin in controllers, chose the port you want to setup:

Retroarch

Chose the one you want to be Port 1 in Device Index, and see if the controls are recognized. If not, proceed with each individual control or go ahead with Set All Controls.

 

Latency

Retroarch

You want the best experience possible:

  • no GPU Sync
  • no delay
  • no run-ahead
  • Polling behavior = whatever it does not change a thing
  • all the rest default

 

AI Service = OFF

Retroarch AI service OFF

AI Service feature allows users to play games written in a foreign language, or add text voice-overs automatically. This uses OCR (optical character recognition), machine translation, and text-to-speech. While these technologies can’t provide the same level of accuracy as curated content, it can go quite far. Machine translation can give a good gist of what’s being said, especially for some language pairs, and text-to-speech can be of great benefit for accessibility.

AI Service needs a hotkey that you press when you need it. It will send a screenshot to a url of your choice and feed you the result back, whether it’s speech or a picture overlay.

More information here

 

Wrapping Up

Next step is … Update cores, assets, databases, import games and play with Retroarch.

  • Auto-Update Retroarch
  • Import Games

 

 

Install Kubernetes control-plane On Ubuntu 20.04

As much as you try, as much as you read their wiki, you will get nowhere until you spend a few hours and open a dozen links. It’s broken from the get go, yes. Installing it without certain parameters just won’t work. This recipe will save you hours. This recipe is just for the first master control-place.

Continue reading Install Kubernetes control-plane On Ubuntu 20.04

Your Own Cheap VPN for $1/mo

People are (rightfully) freaking out about their privacy as the Senate voted the law S.J. 34 to let internet providers share your private data with advertisers. While it’s important to protect your privacy, it doesn’t mean that you need it at all times. Also, VPN is a service that is not free, but I found you the CHEAPEST DEAL AVAILABLE TODAY: therefore, let’s create a VPN in 15mn for $1/mo!

Read more: Your Own Cheap VPN for $1/mo

This guide will show you step by step how to do it with Virmach but this is valid for any other brand, provided you accept the price. The whole point is to use a script that does it for you, so you don’t have to spend a whole day doing it. Yes, setting up a VPN Server by hand is very complicated.

 

1. Get a KVM Server for $1/mo at Virmach

This is the crapiest, cheapest KVM provider I could find as of March 2020. Unfortunately they do not offer $1/mo plans anymore, because it depends on availability. I highly doubt they ever offered these plans in large quantities but you know how business work, yeah?

Select the cheapest KVM from the Virmach plans available here.

virMarch-cheapest-server
You may have a warning saying they are out of stock but they promise to charge you only $1/mo.
Option 2 would be a slightly better bandwidth at $1.25:

virMarch-cheapest-server-2

 

2. Create a RedHat, Debian, Ubuntu or CentOS server

Versions required for the script to work: Supported distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora

  • Ubuntu 18.04 or higher
  • Debian 9 or higher
  • CentOS 7 or higher
  • Fedora any version

virmach-distro-compatibles

The script that will create the OpenVPN server for you needs to be run on the major versions above. Among the limited options that Virmach offers you for that price, as of September 2021, choose one of these:

The versions shown above are the lowest versions compatible with the script. The rest of the setup in straightforward. Take note of the root password they give you and that’s it.

3. Initial Setup (optional)

Virmach will provide you a root access with a password. All the steps below are optional, especially if you already own an IaaS Cloud server access.

A. Update the System (advised)

The commands below are for Debian/Ubuntu systems:

apt update -y
apt upgrade -y
apt autoremove -y
apt clean -y

That’s it. Most systems are already configured for IPv4 Forwarding so you should be good to go.

B. Setup SSH (optional)

You can change the remote SSH access to RSA key access only instead of using a password, by setting up your identity and the SSHd daemon.

First you need to create your SSH identity (you could also force copy your own from another server):

ssh-keygen -f ~/.ssh/id_rsa

Next, add your public RSA key in ~/.ssh/authorized_keys (there are 3 ways to do that but this one is straightforward):

echo ssh-rsa AAAAB3Nza...IsFA0eGz name>~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Next, tweak the SSHd config file:

sed -i -e "s/^#PasswordAuthentication yes/PasswordAuthentication no/g" /etc/ssh/sshd_config || sed -i -e "s/^PasswordAuthentication yes/PasswordAuthentication no/g" /etc/ssh/sshd_config

4. Install OpenVPN

To install OpenVPN and all the dependencies including EasyRSA, simply execute this command:

wget https://git.io/vpn -O openvpn-install.sh && bash openvpn-install.sh

If using Ubuntu 16.04, another script is necessary:

wget https://git.io/vpn1604 -O openvpn-install.sh && bash openvpn-install.sh

That’s it. Now, get the content of the OVPN file generated for your clients:

cat /root/virmach1.ovpn

5. Setup the Clients

Windows Client

With the OVPN file you got, simply import it in OpenVPN GUI and you’re good to go!

DD-wrt Client

This guide will show you how to setup this OpenVPN connection on your home Dd-wrt router so the Wi-Fi is VPN-ized!

 

Wrapping Up

Let me know in the comments how long it actually took you. It took me much more than 15mn because I was writing the guide at the same time, but I think that 15mn altogether is a fair guess.

As of today, March 2020, Virmach let you pay $1 for a $1.25/mo plan because they “ran out” of cheap plans, but you are limited to 1 only. Also, no other location than within the USA are available, which defeats the purpose of a VPN when you live in the USA. What you really want is an access point in Europe.

Ultimate MSDOS interactive dynamic menu

This is the Ultimate MSDOS interactive dynamic menu with Powershell quirk. Vertical menu controlled by cursor keys via Powershell quirk + Horizontal carousel to select each option’s value.

Linus Tech Tips

Purpose of vertimenu

Presentation of vertimenu

This batch is a compilation of all the crazy interactive menu examples
by Antonio Perez Ayala from https://www.dostips.com/forum/viewtopic.php?f=3&t=6936

  • By selecting options in the menu, obtain a list of variables install{product} and version{product}.
  • {product} can be anything you like: AA, BB etc
  • Then you process these in your own routines

Usage for vertimenu

Usage:

  • The batch below is used in a modified way to download stuff from FTP or OneDrive.
  • Drop it on some server, then it’s using a preset of versions to choose from
  • Then it is supposed to download formated ini files names: install-{product}-{version}-platform|product.ini that contain the actual files to download
  • Use it for whatever your like!

Features of vertimenu

  • Vertical menu controlled by cursor keys via Powershell quirk
  • Horizontal carousel to select each option’s value (we call it version)
  • Win10 compatible colors with colorless selector fallback for Vista/2012 – findstr trick is disabled
  • Tabbed indentation with parent and children
  • Jump over spacers and disabled options!
  • Multicolumns for selected version and highest detected version
  • Windows like children options toggle when Parent options are toggled
  • Auto attribution of version value to Menu option children without version
  • Dynamic indentation and menu resize
  • (Un)limited number of tab levels
  • CSV-like controlled options
  • Includes _PS_Resize trick to fit the content of the menu
  • Includes comments! How unusual…
  • Maximum 30 options

Source Code & Git for vertimenu

https://github.com/audioscavenger/vertimenu-msdos-dynamic-menu

@echo OFF
if "%~1" equ "vmenu_OptionSelection" goto :%~1
pushd %~dp0
setlocal enabledelayedexpansion

:top 
set DEMO=
set DEBUG=
set VERBOSE=
...

@echo OFF
if "%~1" equ "vmenu_OptionSelection" goto :%~1
pushd %~dp0
setlocal enabledelayedexpansion

:top
set DEMO=
set DEBUG=
set VERBOSE=
set PAUSE=echo.
:: enable the line below to debug and pause at places of your choice
REM set PAUSE=pause
set POPUP=false
IF DEFINED DEBUG set VERBOSE=true
verify on

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set author=audioscavenger@it-cooking.com
set version=5.1.10
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Purpose
::        This batch is a compilation of all the crazy interactive menu examples
::        by Antonio Perez Ayala on https://www.dostips.com/forum/viewtopic.php?f=3&t=6936
::
::        By selecting options in the menu, obtain a list of variables install{product} and version{product}
::        {product} can be anything you like: AA, BB etc
::        Then you process these in your own routines
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Features
::        Vertical menu controlled by cursor keys via Powershell quirk
::        Horizontal carousel to select each option's value (we call it version)
::        Win10 compatible colors with colorless selector fallback for Vista/2012 - findstr trick is disabled
::        Tabbed indentation with parent and children
::        Jump over spacers and disabled options!
::        Multicolumns for selected version and highest detected version
::        Windows like children options toggle when Parent options are toggled
::        Auto attribution of version value to Menu option children without version
::        Dynamic indentation and menu resize
::        (Un)limited number of tab levels
::        CSV-like controlled options
::        Includes _PS_Resize trick to fit the content of the menu
::        Includes comments! How unusual...
::        Maximum 30 options
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: * TODO
::     1. actually include hidden tools choice in the list because selector menu thinks nothing selected if only that
::     1. add -r to arguments to REFRESH remoteVersionsAvailable
::     1. evolve :arguments to getopt
:: 5.1 enhancements and bug-fixes:
::     1. added local versions detection!
::     2. finally a version flip-switch that's blocked at the edges
::     5. bug fix: deselect a tabbed option would disable the next ones
::     7. protect all [x] comparisons between quotes
::    10. introduce export[%%i] to include or not selected products (used for menu parents)
:: 5.0 enhancements and bug-fixes:
::     1. integrated magic vmenu from https://www.dostips.com/forum/viewtopic.php?f=3&t=6936
::     2. added powershell winsize to resize window
::     3. simplified labels definition, format and colors
::     4. unset PAUSE on DEBUG, one may want DEBUG REMOTE
::     5. auto jump disabled option with DO WHILE emulation
::     7. enable disabled sub-options when selecting top option
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: set local folders
set rootDir=%CD%
set LOGS=%rootDir%\logs
md %LOGS% 2>NUL
set LOG=%LOGS%\%~n0.log
set EXAMPLE_LocalFolder=%rootDir%\install-product-files

:: TMP files management - RANDOM everytime because of file lock by our friend cmd.exe
set TMPFILE=%LOGS%\%~n0.%RANDOM%.tmp.txt
set TMPWARN=%LOGS%\%~n0.warning.%RANDOM%.tmp.log
set TMPERR=%LOGS%\%~n0.error.%RANDOM%.tmp.log
set remoteVersionsAvailable=%LOGS%\%~n0.remoteVersionsAvailable.txt
set versionsSelected=%LOGS%\%~n0.versionsSelected.tmp.txt

:start
IF DEFINED DEBUG echo %TIME% :start

:: when connected remotely via a LocalSystem agent, USERNAME=COMPUTERNAME$
IF [%USERNAME:~-1%]==[$] set AUTOMATED=true
IF NOT [%1]==[] (set AUTOMATED=true) ELSE (title %~n0 %version% - %COMPUTERNAME% - %USERNAME%@%USERDNSDOMAIN% %USERDOMAIN%)
IF DEFINED AUTOMATED call :arguments %*
IF %ERRORLEVEL% EQU 99 exit /b 0
call :detect_winVersion
IF NOT DEFINED AUTOMATED call :set_colors
call :prechecks

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: defaults - set your defaults here
:: include your default values here
:defaults
:: Windows Vista / 2012 and below: fallback for absence of colors
IF "%RC%"=="" (set "cursor=^>") ELSE set "cursor= "

:: width of your menu labels
set labelWidth=40

:: width of your indentations
set tabWidth=2

:: how many levels for your sub-menus?
set maxLevels=5

:: comment the line below to always export every options so the "export" column won't be used
set alwaysExportAll=alwaysExportAll

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: defaults

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: menu content
:menuContent

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: pre-selections: 1st field, chose what is pre-selected at start;
:: Each variable 'installXX' is a selector in the menu
:: No field can be NULL because for loop considers multiple separators as a single one
:: field 5 = set of versions available for each option in the nenu
:: It is good practice to set field 3 = product codes all the same length
:: field 4 = color includes a space as first char for color backward compatibility with previous versions of Windows
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: in this example, we also have submenu parents which won't be used, 
:: it all depends on what you want to do after the options are passed back to main.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
set "switch="
set "endHeader="
REM ::                    1=b ;  2=c   ;  4=d   ;   3=e    4=f  ;   5=g    ;  6=h
REM :: labelLine format = tab ; select ; export ; product color ; versions ; label
REM set options=1;x;Tools;   ;tools + Rkit ^(cannot remove^)
set           options=1;x;Y;AA; %y%;3.3.0.12 3.2.4.0;AA label             
set options=%options%/2; ;Y;AA1; %y%;3.5.5.3-US;AA1              
set options=%options%/1; ;Y;BB; %y%;1.6.2.11 1.6.2.8;BB label             
set options=%options%/1; ;Y;CC; %y%;5.1.0.67 5.1.0.52 5.1.0.49;CC label             
set options=%options%/2; ;Y;CC1; %y%;5.1.2.30-US 5.1.1.1-US;CC1              
set options=%options%/0; ;Y;spacer; %w%; ; This line is a spacer
set options=%options%/1; ;Y;PA; %y%;5.0.1.34;PA label             
set options=%options%/2; ;Y;PA1; %w%;UFRII_v2.10 PCL6_v2.00;PA1 Submenu 1 xyz         
set options=%options%/2; ;N;PA2ParentWontBeUsed; %w%; ;PA2 Submenu Parent       
set options=%options%/3;x;Y;PA3; %w%;PCL6;PA3 Submenu 2-1 xyz      
set options=%options%/3;x;Y;PA4; %w%;UFRII;PA4 Submenu 2-2 xyz      
set options=%options%/2; ;Y;PB; %w%;1 2 3;xPB label                
set options=%options%/1; ;N;SQLParentWontBeUsed; %w%;2017 2016 2014;SQL Parent menu                
set options=%options%/2;x;Y;SQL1; %w%; ;SQL1                           
set options=%options%/2;x;Y;SQL2; %w%; ;SQL2                           
set options=%options%/0; ;Y;spacer; %w%; ; This line is a spacer
set options=%options%/1; ;Y;EXAMPLEsmthAndReloadMenu; %w%; ;Update available remote versions     

:: calculate number of options here
REM for %%a in ("%options:/=" "%") do set /A lastOption+=1
for %%a in ("%options:/=" "%") do set /A totalOptions+=1

:: :detect_local_installs must be done before options definition and after options defaults
call :detect_local_installs %*

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: menu content
IF NOT DEFINED AUTOMATED call :winsize 120 40 120 9997
REM IF NOT DEFINED DEMO call :EXAMPLE_setupSomeStuff
REM IF DEFINED AUTOMATED call :EXAMPLE_alterVersionsAvailableInMenu & goto :main


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: menu loop
:menu
:: you could alter the menu options before loading it, here:
REM call :EXAMPLE_alterVersionsAvailableInMenu

:: define tabbed indentations spaces here:
call :vmenu_setTabbedSpaces

:: menu needs to be redrawn with a goto
goto :vmenu_header
REM IF /I NOT [%choice%]==[n] goto :menu
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: menu loop


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: main program
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: main program
:: from here, no user interaction no more
:main
IF DEFINED VERBOSE echo %TIME% :main

IF DEFINED DEBUG echo call :vmenu_decodeOptions %select%
:: STEP 1: decode binary shift encoded options + setup install{product} and version{product} variables
call :vmenu_decodeOptions %select%

:: STEP 2: (optional) copyParentVersions from Parent menu items into their children
:: the EXAMPLE below will for example, copy the Parent submenu version into its children with empty version.
call :vmenu_copyParentVersions-EXAMPLE

:: EXAMPLE: (optional) menu loop after alteration
:: if installEXAMPLEsmthAndReloadMenu is chosen as an option, 
:: it will call a routine that will alter the menu/versions and then reload the menu
IF "%installEXAMPLEsmthAndReloadMenu%"=="x" call :installsmthAndReloadMenu-EXAMPLE & goto :menu

:: STEP 3: (optional) visualize the options finally selected with their version
call :vmenu_listOptions %select%

:: STEP 4: (optional) validate each version
:: EXAMPLE: :select_versions routine will ask user to post-modify/validate each version selected
call :select_versions

echo menu selection is over. Call your routines here...
REM call :routine1
REM call :routine2
REM call :routine3

pause
goto :end
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: main program
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: main program



:installsmthAndReloadMenu-EXAMPLE
echo do something here to alter the menu options
goto :EOF

:arguments %*
IF DEFINED DEBUG echo %HIGH%%c% %~0 %END%%c% %* %END%

IF /I [%1]==[version]           echo version=%version% & exit /b 99

call :USAGE & exit /b 99
goto :EOF

:USAGE
echo Usage:    %~n0 [ help ^| version ^| whatever you like]
goto :EOF


::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: start of magic vmenu
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:vmenu_header
  cls
  IF DEFINED DEBUG echo %TIME% %~0
  REM echo(%nbsp%
  call :your-logo-here
  REM echo/
  REM echo Example of Check List / Radio Button
  REM echo/
  echo Move selection lightbar with these cursor keys:
  echo Home/End = First/Last, Up/Down = Prev/Next, or via option's first letter, Left/Right = set Version
  echo                                                     selected     local
  echo      [x] tools + Rkit ^(cannot remove^)             ----------- + ----------
  %endHeader%

  if defined switch set "switch=/R"
  call :vmenu_CheckList select="%options%" %switch%
  echo/
  echo/
  if "%select%" equ "0" goto :vmenu_endProg
  if DEFINED DEBUG echo   DEBUG: Binary Options sum: %select%
  
:: example loop
goto :main
:vmenu_endProg
goto :EOF


:vmenu_CheckList select= "option1/option2/..." [/R]
setlocal EnableDelayedExpansion
:: vmenu subroutine activates a CheckList/RadioButton form controlled by cursor control keys
:: RadioButton is now certainly broken but I keep its original code for history purpose

:: %1 = Variable that receive the selection
:: %2 = Options list separated by slash
:: %3 = /R (switch) = Radio Button (instead of Check List)

:: Process /R switch
if /I "%~3" equ "/R" (
  set "Radio=1"
  set "unmark=( )" & set "mark=(o)"
) else (
  set "Radio="
  set "unmark=[ ]" & set "mark=[x]"
)

:: Separate options
set "options=%~2"
set "lastOption=0"
for %%a in ("%options:/=" "%") do (
  set /A lastOption+=1
  set labelLine=%%~a

  REM ::                    1=b ;  2=c   ;  4=d   ;   3=e    4=f  ;   5=g    ;  6=h
  REM :: labelLine format = tab ; select ; export ; product color ; versions ; label
  for /F "tokens=1-6* delims=;" %%b in ("!labelLine!") do (
    set "tab[!lastOption!]=%%~b"
    set "tabs[!lastOption!]=!tabSpaces[%%~b]!"

    REM :: grab selected products and options: an "x" marks the bounty
    set "select[!lastOption!]=[%%~c]"
    set "install%%e=%%~c"
    
    REM :: compatibility with Vista/Server 2012 and below: no colors available
    set export[!lastOption!]=%%~d
    
    REM :: compatibility with Vista/Server 2012 and below: no colors available
    set color=%%~f
    set labelColor[!lastOption!]=!color:~1!
    
    REM :: versions used in the right column carousel
    set "versions=%%~g"
    set "versions[!lastOption!]=%%~g"
    REM :: IMPORTANT: this is where we get the local detected versions found by :detect_local_installs
    call set "versionsFound[!lastOption!]=%%versionsFound%%~e%%"

    set numVersions=0
    set firstVersion=
    set move2Version[!lastOption!]=0
    for %%v in (!versions!) DO (
      set /A numVersions+=1
      IF "!firstVersion!"=="" (
        set firstVersion=done
        set "version[!lastOption!]=%%v"
        set move2Version[!lastOption!]=1
      )
    )
    set numVersions[!lastOption!]=!numVersions!
    REM set labelVersions[!lastOption!]=!thisLabelVersions!
    
    REM :: add spaces after label to LEFT trim it after
    set label=%%~h                                             
    REM :: calculate the label width and setup toggles
    IF %%~b EQU 0 (
      set "toggle[!lastOption!]=off"
    ) ELSE (
      set "toggle[!lastOption!]=on"
      
      REM :: auto-calculate label width based on tabbing
      call set "option[!lastOption!]=%%label:~0,!labelWidth[%%~b]!%%"
    )
  )

  REM :: Below we setup selected menu item with 
  REM :: the line below is real genius as it's a Unix like command expansion in a variable!
  call set "moveSel[%%option[!lastOption!]:~0,1%%]=set sel=!lastOption!"
)
for /L %%j in (1,1,%totalOptions%) DO IF !tab[%%j]! EQU 1 call :vmenu_toggleColor %%j %totalOptions%

if defined Radio set "select[1]=%mark%"

:: Define powershell vmenu working variables
for %%a in ("Enter=13" "Esc=27" "Space=32" "Endd=35" "Home=36" "LeftArrow=37" "RightArrow=39" "UpArrow=38" "DownArrow=40" "LetterA=65" "LetterZ=90") do set %%a
set "letter=ABCDEFGHIJKLMNOPQRSTUVWXYZ"

:: findstr trick - for Server 2012 and under
REM for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"
REM echo %BS%%BS%%BS%%BS%%BS%%BS%      >_

:: Define movements for standard keys
:: Also define left/right options for versions
set "sel=1"
set "moveSel[%Home%]=set sel=1"
set "moveSel[%Endd%]=set sel=%lastOption%"
set "moveSel[%UpArrow%]=set /A sel-=^!^!(sel-1)"
set "moveSel[%DownArrow%]=set /A sel+=^!^!(sel-lastOption)"

:: Read keys via PowerShell  ->  Process keys in Batch
set /P "=Loading vmenu..." < NUL
PowerShell -executionPolicy bypass -Command ^
  Write-Host 0;  ^
  $validKeys = %Endd%..%Home%+%LeftArrow%+%RightArrow%+%UpArrow%+%DownArrow%+%Space%+%Enter%+%Esc%+%LetterA%..%LetterZ%;  ^
  while ($key -ne %Enter% -and $key -ne %Esc%) {  ^
    $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown').VirtualKeyCode;  ^
    if ($validKeys.contains($key)) {Write-Host $key}  ^
  }  ^
%End PowerShell%  |  "%~F0" vmenu_OptionSelection

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: THIS IS WHERE WE PASS SELECTED OPTIONS BACK TO :MAIN
:: %1 is actually "select" passed as 1st arg to this routine, since '=' sign counts as MSDOS separator
:: The trick below is called Passing variables from one routine to another: https://ss64.com/nt/endlocal.html
:: By attaching '&' to endlocal, we are able to SET a (group of) variables just before the localisation is ended
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
endlocal & set "%~1=%errorlevel%"

:: another way of passing more variables to the parent shell:
REM Endlocal&(
REM set "%~1=%errorlevel%"
REM set "versions=%version[1]% %version[2]% %version[3]%")
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:: findstr trick
REM del _
exit /B


:vmenu_toggle sel maxSel
if defined Radio (
  set "select[%Radio%]=%unmark%"
  set "select[%1]=%mark%"
  set "Radio=%1"
) else (
  if "!select[%1]!" equ "%unmark%" (
    set "select[%1]=%mark%"
  ) else (
    set "select[%1]=%unmark%"
  )
  
  call :vmenu_toggleColor %1 %2
)
exit /B


:vmenu_toggleColor sel maxSel
REM :: below we en(dis)able sub-options based on their tab[%%j] value
set lastOne=0
set /A "nextSel=%1+1"
IF "!select[%1]!"=="%mark%" (set toggleNext=on) ELSE set toggleNext=off

for /L %%j in (%nextSel%,1,%2) DO (
  REM :: stop processing if %%j < lastOne
  IF NOT %%j LEQ !lastOne! (
    REM :: stop processing if next tab is ==root
    IF !tab[%%j]! EQU !tab[%1]! exit /b
    REM :: stop processing if next tab is root==1 or ==itself
    IF !tab[%%j]! LSS 2 exit /b
    
    :: at this point, %%j tab is 100% > sel tab
    set "toggle[%%j]=%toggleNext%"
    set lastOne=%%j
    IF "!select[%%j]!%toggleNext%"=="%unmark%on" call :vmenu_toggleColor %%j %2
  )
)
exit /B


:vmenu_OptionSelection
setlocal EnableDelayedExpansion

rem Wait for PS code start signal
set /P "keyCode="
set /P "="

set "endHeader=exit /B"
:vmenu_ReDraw
:: vmenu_ReDraw draws every line after each key press
:: Clear the screen and show the list:
call :vmenu_header

  REM :: anti flicker trick: doesn't work
  REM echo(%nbsp%
  < NUL (for /L %%i in (1,1,%lastOption%) do (
      set "num=  %%i"
      set "labelVersion=!version[%%i]!                    "
      set "labelversionsFound=!versionsFound[%%i]!                    "
      IF DEFINED DEBUG (
        set ddebug=!toggle[%%i]!    !move2Version[%%i]!
        set ddebugToggle=!toggle[%%i]!    !move2Version[%%i]!
        echo !ddebugToggle!>>ggg
      )
      if !tab[%%i]! EQU 0 (
        REM :: spacer
        echo.!ddebug!
      ) ELSE (
        if "!toggle[%%i]!" equ "off" (
          REM :: this line is disabled:
          echo  !num:~-2!!tabs[%%i]!%HIGH%%k%!select[%%i]! !option[%%i]! !labelVersion:~0,11!%END% ^| !labelversionsFound:~0,20!!ddebug!
          ) ELSE (
          if "%%i" equ "%sel%" (
            REM :: this line is active AND highlighted
            
            REM :: findstr trick
            REM set /P "=%k%!tab[%%i]!%END%!num:~-2! !select[%%i]!  "
            REM findstr /A:17 . "!option[%%i]!\..\_" NUL
            
            REM :: We show horizontal carousel only if there is more than one version available;
            REM :: also we want to show the direction where the other versions are
            IF !numVersions[%%i]! GTR 1 (
              IF !move2Version[%%i]! EQU !numVersions[%%i]! (
                REM :: last version is shown
                echo %cursor%!num:~-2!!tabs[%%i]!!select[%%i]!%cursor%%RC%%k%!option[%%i]!%END%^<%RB%%w%!labelVersion:~0,11!%END%]^| !labelversionsFound:~0,20!!ddebug!
              ) ELSE (
                IF !move2Version[%%i]! EQU 1 (
                  REM :: first version is shown
                  echo %cursor%!num:~-2!!tabs[%%i]!!select[%%i]!%cursor%%RC%%k%!option[%%i]!%END%[%RB%%w%!labelVersion:~0,11!%END%^>^| !labelversionsFound:~0,20!!ddebug!
                ) ELSE (
                  REM :: middle versions are shown
                  echo %cursor%!num:~-2!!tabs[%%i]!!select[%%i]!%cursor%%RC%%k%!option[%%i]!%END%^<%RB%%w%!labelVersion:~0,11!%END%^>^| !labelversionsFound:~0,20!!ddebug!
                )
              )
            ) ELSE (
              REM :: only one version is shown
              echo %cursor%!num:~-2!!tabs[%%i]!!select[%%i]!%cursor%%RC%%k%!option[%%i]!%END%[!labelVersion:~0,11!%END%]^| !labelversionsFound:~0,20!!ddebug!
            )
          ) else (
            REM :: this line is active but not highlighted
            IF "!select[%%i]!"=="%unmark%" (set versionColor=) ELSE set versionColor=%HIGH%
            echo  !num:~-2!!tabs[%%i]!!select[%%i]! !labelColor[%%i]!!versionColor!!option[%%i]! !labelVersion:~0,11!%END% ^| !labelversionsFound:~0,20!!ddebug!
          )
        )
      )
    )
  )
  echo/
  set /P "=Space=(De)Select, Enter=Continue, Esc=Cancel" < NUL

  REM :: Get a keycode from PowerShell
  set /P "keyCode="
  set /P "="

  REM :: Process it: check for action keys
  if %keyCode% equ %Enter% goto :vmenu_encodeSelection
  if %keyCode% equ %Esc% exit 0
  
  REM :: we process Left/Right only if numVersions > 1
  IF !numVersions[%sel%]! GTR 1 (
    set lastVersion=0
    if %keyCode% equ %LeftArrow% (
      for %%v in (!versions[%sel%]!) DO (
        set /A lastVersion+=1
        IF "!version[%sel%]!"=="%%v" IF !lastVersion! GTR 1 set /A "move2Version[%sel%]-=1"
      )
    )
    if %keyCode% equ %RightArrow% (
      for %%v in (!versions[%sel%]!) DO (
        set /A lastVersion+=1
        IF "!version[%sel%]!"=="%%v" IF !lastVersion! LSS !numVersions[%sel%]! set /A "move2Version[%sel%]+=1"
      )
    )
    
    REM :: below we flip-switch the version after Left/Right is pressed
    IF !lastVersion! GTR 0 (
      REM :: The trick below can be used to rotate versions indefinitely back and forth:
      REM IF !move2Version! GTR !lastVersion! set move2Version=1
      REM IF !move2Version! LEQ 0 set move2Version=!lastVersion!
      set lastVersion=0
      
      REM :: Cannot use '!' in the tokens= part, that's too bad:
      REM for /f "tokens=%move2Version[!sel!]%" %%v in ("!versions[%sel%]!") DO set "version[%sel%]=%%v" & set "versionsFound[%sel%]=%%v"
      
      REM :: Using ! for the calculated token doesn't work, you need a full-fledge loop with increment
      for %%v in (!versions[%sel%]!) DO (
        set /A lastVersion+=1
        IF !lastVersion! EQU !move2Version[%sel%]! set "version[%sel%]=%%v"
      )
    )
  )
  
  REM :: below we (un)mark options after space is pressed
  if %keyCode% equ %Space% (
    call :vmenu_toggle %sel% %lastOption%
    goto :vmenu_ReDraw
  )

  REM :: Process it: check for move keys
  if %keyCode% lss %LetterA% goto :vmenu_jumpSelection
  REM :: Below we process pressed key from A-Z
  REM :: Last Letter option wins when multiple labels start with same Letter
  set /A keyCode-=LetterA
  set "keyCode=!letter:~%keyCode%,1!"
  :vmenu_jumpSelection
  !moveSel[%keyCode%]!
  REM :: jump next one if this is a spacer - first and last options cannot be a spacer
  REM :: BUG: this works only for Arrows Up/Down, for Letters you actually can end on a disabled option
  if "!toggle[%sel%]!"=="off" !moveSel[%keyCode%]!

  REM :: jump next one if this is disabled - DO WHILE emulation
  REM :: This cannot work if first or last option is disabled
  :vmenu_whileDisabled
  IF DEFINED DEBUG call echo toggle[%sel%]=!toggle[%sel%]! %lastOption%   moveSel[%keyCode%]=!moveSel[%keyCode%]!>>ggg
  REM :: the loop below will jump to next selection that's enabled when pressing a Letter,
  REM :: if the letter leads to a disabled option, by forcing using an Arrow instead
  if "!toggle[%sel%]!" equ "off" (
    if %keyCode% GEQ %LetterA% (
      if %sel% lss %lastOption% (set keyCode=38) ELSE set keyCode=40
      !moveSel[%keyCode%]!
    )
  ) ELSE goto :vmenu_whileEnd
  REM keyCode 40 = up, 38 = down
  if %keyCode% equ 38 (set /A "sel-=1") ELSE set /A "sel+=1"
  goto :vmenu_whileDisabled
  :vmenu_whileEnd

  REM :loop_antiflicker
  REM if "%time:~-1%"=="!time:~-1!" goto :loop_antiflicker
goto :vmenu_ReDraw

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: :vmenu_encodeSelection will create the binary encoded errorlevel used by :vmenu_decodeOptions
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:vmenu_encodeSelection
set "sel="
del /f /q %versionsSelected% >NUL 2>NUL
:: We need to process options in reverse order because that's how we'll pop then out of the errorlevel
for /L %%i in (1,1,%lastOption%) do (
  REM :: we always export all versions because we process every options in reverse order
  echo "!version[%%i]!">>%versionsSelected%
  
  REM :: 1<<x = 2 power x
  REM :: To get the original selections, just for loop in reverse and substract each power values of 2
  REM :: We also make sure we select only those which are not disabled by checking for !toggle[%%i]!
  if "!select[%%i]!!toggle[%%i]!" equ "%mark%on" (
    REM :: We also export only the products tagged "Y" for export unless alwaysExportAll is set
    REM :: Beware: by doing so, you do not export Parent menu items and cannot copy their version into their children in :vmenu_copyParentVersions
    if /I "%alwaysExportAll%"=="alwaysExportAll" (
      set /A "sel+=1<<%%i"
    ) ELSE (
      if /I "!export[%%i]!"=="Y" (
        set /A "sel+=1<<%%i"
      )
    )
  )
)

if NOT DEFINED sel set "sel=0"
:: BUG: there is a MAX value for %sel%: ERRORLEVEL cannot be higher than 1357508192 > 2^30 = 30 options maximum
exit %sel%
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:vmenu_decodeOptions %select%
IF DEFINED DEBUG echo %~0 %1 with lastOption=%lastOption%
:: %binarySum% is a binary addition: sum of all selected options as power of 2
:: BUG: there is a MAX value for binarySum: ERRORLEVEL cannot be higher than 1357508192 > 2^30 = 30 options maximum
set binarySum=%1

:: Separate options
:: TODO: this could be exported as a separate routine since we need that to reverse option selections
set "lastOption=0"
for %%a in ("%options:/=" "%") do (
  REM ::                    1=b ;  2=c   ;  4=d   ;   3=e    4=f  ;   5=g    ;  6=h
  REM :: labelLine format = tab ; select ; export ; product color ; versions ; label
  set /A lastOption+=1
  set labelLine=%%~a
  for /f "tokens=1-6* delims=;" %%b in ("!labelLine!") do (
    set product[!lastOption!]=%%~e
  )
)

:: decode each option
:: %select% is a binary addition: sum of all selected options as power of 2
for /L %%i in (%lastOption%,-1,1) do (
  REM :: overwrite whatever install is detected first:
  call set install!product[%%i]!= 
  set /A "thisOne=1<<%%i"
  REM :: (sign bit -> 0) An arithmetic shift: https://ss64.com/nt/set.html
  REM :: { 1 Lsh 1 = binary 01 Lsh 1 = binary 010   = decimal 2 }
  REM :: { 1 Lsh 2 = binary 01 Lsh 2 = binary 0100  = decimal 4 }
  REM :: { 1 Lsh 3 = binary 01 Lsh 3 = binary 01000 = decimal 8 }
  REM :: etc
  IF !binarySum! GEQ !thisOne! (
    REM :: substract arithmetic shift from binarySum and continue
    REM :: by doing so, we extract each selected option. There is a limit tho:
    set /A "binarySum-=1<<%%i"
    call set install!product[%%i]!=x
  
    REM :: grab selectedVersion for product[%%i]
    set line=0
    for /F %%v in (%versionsSelected%) do set /A line+=1 && IF !line! EQU %%i call set "version!product[%%i]!=%%~v"
    IF DEFINED DEBUG call echo   DEBUG1: Now we can execute option %%i = install!product[%%i]! with version version!product[%%i]!=%%version!product[%%i]!%%
  )
)
goto :EOF

:vmenu_copyParentVersions-EXAMPLE
IF DEFINED DEBUG echo %~0 %1 with lastOption=%lastOption%
:: EXAMPLE: post-process options 
:: This example will attribute parent selection version to its children with empty version.
:: Don't use it if you are OK with products with empty versions
echo.
for /L %%n in (1,1,%lastOption%) do (
  call set "install=%%install!product[%%n]!%%"
  IF "!install!"=="x" (
    call set "thisVersion=%%version!product[%%n]!%%"
    IF NOT "!thisVersion!"=="" (
      set lastVersion=!thisVersion!
    ) ELSE (
      call set "version!product[%%n]!=!lastVersion!"
    )
    IF DEFINED DEBUG call echo   DEBUG2: Now we can execute option %%n = install!product[%%n]! with version version!product[%%n]!=%%version!product[%%n]!%%
  )
)
goto :EOF

:vmenu_setTabbedSpaces
for /L %%L in (1,1,%maxLevels%) DO (
  for /L %%s in (1,1,%tabWidth%) DO (
    set "tabSpaces=!tabSpaces! "
  )
  set "tabSpaces[%%L]=!tabSpaces!"
  set /A "labelWidth[%%L]=labelWidth-thisTabWidth"
  set /A thisTabWidth=thisTabWidth+tabWidth
)

goto :EOF

:vmenu_listOptions %select%
IF DEFINED DEBUG echo %~0 %1 with lastOption=%lastOption%
set binarySum=%1

:: %select% is a binary addition: sum of all selected options as power of 2
echo.
for /L %%i in (%lastOption%,-1,1) do (
  set /A "thisOne=1<<%%i"
  IF !binarySum! GEQ !thisOne! (
    set /A "binarySum-=1<<%%i"
    IF DEFINED DEBUG call echo   DEBUG3: Now we can execute option %%i = install!product[%%i]! with version version!product[%%i]!=%%version!product[%%i]!%%
  )
)
goto :EOF

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: end of magic menu
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


:prechecks
IF DEFINED DEBUG echo %HIGH%%c% %~0 %END%%c% %* %END%
del /f /q %LOGS%\%~n0.*.tmp.* 2>NUL

for %%x in (powershell.exe) do (set powershell=%%~$PATH:x)
IF NOT DEFINED powershell call :error %~0: powershell NOT FOUND

:: test %TMP% exist and write access, i've seen cases where %TMP% is set but actually don't exist
echo]>%TMPFILE%
IF %ERRORLEVEL% NEQ 0 call :error %~0: NO writeable folder found, please logoff to reload environment... EXIT & %PAUSE% & exit

goto :EOF



:::::::::::::::::::::::::::::::::::::::::::::::: technical functions
:detect_winVersion
IF DEFINED DEBUG echo %HIGH%%c% %~0 %END%%c% %* %END%
set osType=workstation
wmic os get Caption /value | findstr Server >%TMPFILE%
IF %ERRORLEVEL% EQU 0 set osType=server

:: https://www.lifewire.com/windows-version-numbers-2625171
IF [%osType%]==[workstation] (
  ver | findstr /C:"Version 10.0" && set WindowsVersion=10& goto :EOF
  ver | findstr /C:"Version 6.3" && set WindowsVersion=8.1& goto :EOF
  ver | findstr /C:"Version 6.2" && set WindowsVersion=8& goto :EOF
  ver | findstr /C:"Version 6.1" && set WindowsVersion=7& goto :EOF
  ver | findstr /C:"Version 6.0" && set WindowsVersion=Vista& goto :EOF
  ver | findstr /C:"Version 5.1" && set WindowsVersion=XP& goto :EOF
) ELSE (
  for /f "tokens=4" %%a in (%TMPFILE%) do set WindowsVersion=%%a
)
goto :EOF

:set_colors
set colorCompatibleVersions=-8-8.1-10-2016-2019-
IF DEFINED WindowsVersion IF "!colorCompatibleVersions:-%WindowsVersion%-=_!"=="%colorCompatibleVersions%" goto :EOF


goto :EOF
:: BUG: some space are needed after :set_colors


:your-logo-here
echo.
echo.
echo    %y%                    __   __                  __        __   ___  __  tm
echo    %y%                   ^|  \ /  \ ^|  ^| ^|\ ^| ^|    /  \  /\  ^|  \ ^|__  ^|__) 
echo    %y%                   ^|__/ \__/ ^|/\^| ^| \^| ^|___ \__/ /~~\ ^|__/ ^|___ ^|  \ 
echo    %c%   ,;            
echo    %c% `7MMpMMMb.pMMMb.
echo    %c%   MM    MM    MM
echo    %c%   MM    MM    MM
echo    %c%   MM    MM    MM
echo    %c% .JMML  JMML  JMML
echo.%END%
goto :EOF


:error "msg"
echo.%r%
echo ==============================================================
echo %HIGH%%r%  ERROR:%END%%r% %*
IF /I [%2]==[powershell] echo %y%Consider install Management Framework at https://support.microsoft.com/en-us/help/968929/ %r% 1>&2
echo ==============================================================
echo.%END%
IF NOT DEFINED AUTOMATED pause
exit
goto :EOF
:::::::::::::::::::::::::::::::::::::::::::::::: technical functions


:select_versions
IF DEFINED DEBUG echo %HIGH%%c% %~0 %END%%c% %* %END%

:: manually re-validate each version:
set choice=n
set /P choice=Would you like to manually validate each version? [%HIGH%%y%%choice%%END%] 
IF /I NOT "%choice%"=="n" (
  for /L %%n in (1,1,%lastOption%) do (
    call set "install=%%install!product[%%n]!%%"
    IF "!install!"=="x" (
      call set "thisVersion=%%version!product[%%n]!%%"
      set /P version!product[%%n]!=version!product[%%n]!? [%HIGH%%m%!thisVersion!%END%] 
    )
  )
)

:: manually re-validate each product's download file:
IF %POPUP%==true (set havePOPUP=y) ELSE (set havePOPUP=n)
set /P havePOPUP=POPUP files list before download? [%HIGH%%y%%havePOPUP%%END%] 
IF /I "[%havePOPUP%]"=="[y]" (set POPUP=true) ELSE (set POPUP=false)

:: EXAMPLE: post-process some product's versions to shorten them for some reason:
:: PRODUCTx short versions are used in main download section for platform.ini files: !%%ax%!
for %%P in (BB CC PA) DO call set %%Px=%%%%Pversion:~0,1%%

:: EXAMPLE: special cases for some other products:
set SQL1x=%SQL1version%
set SQL2x=%SQL1version%
set AAx=%AAversion:~0,3%

echo.
goto :EOF



:detect_local_installs %*
IF DEFINED DEBUG echo %HIGH%%c% %~0 %END%%c% %* %END%

:: detect what's present to pre-check options
:: 3 different detection patterns: your needs, your choice!
for %%P in (AA BB CC PA AA1 CC1) DO (
  for /f "tokens=3 delims=-" %%v in ('dir /b install-%%P-*-product.ini 2^>NUL') DO (
    CALL set install%%P=x
    CALL set versionsFound%%P=%%v
  )
)
for %%P in (PA1 PA2 PA3 PA4) DO (
  IF EXIST %EXAMPLE_LocalFolder%\PA\%%P\ (
    CALL set install%%P=x
    CALL set versionsFound%%P=%%v
  )
)
for %%P in (SQL1 SQL2) DO (
  for /f "tokens=3 delims=-" %%v in ('dir /b install-%%P-*-platform.ini 2^>NUL') DO (
    CALL set install%%P=x
    CALL set versionsFound%%P=%%v
  )
)
goto :EOF



:: :winsize  winWidth  winHeight  bufWidth  bufHeight
:winsize
:: Console Resize values via PowerShell (changeable)
SET "_PSResize=100 54 100 9997"
IF NOT [%4]==[] SET "_PSResize=%1 %2 %3 %4"

:: Check for powershell via PATH variable
REM POWERSHELL "Exit" >NUL 2>&1 && SET "_PS=1"
REM IF NOT DEFINED _PS (ECHO No&do something) ELSE (ECHO Yes&do something)
 
:: PS-Console Resizing
REM IF DEFINED _PS CALL:_PS_ReSize %_PSRESIZE%
CALL :_PS_ReSize %_PSRESIZE%
goto :EOF

:: :_PS_Resize  winWidth  winHeight  bufWidth  bufHeight
:_PS_Resize
:: Mode sets buffer size-not window size
MODE %1,%2

:: resize
powershell -executionPolicy bypass -Command "&{$H=get-host;$W=$H.ui.rawui;$B=$W.buffersize;$B.width=%3;$B.height=%4;$W.buffersize=$B;}"
goto :EOF

:end
IF DEFINED DEBUG echo :end
echo %DATE% %TIME% %HIGH%%c% %~0 %END%%c% %* %END% ------- THE END

pause
del /f /q %LOGS%\%~n0.*.tmp.* 2>NUL

Licence: GNU GPL3, share it or leave it

 

Resolve SSL Vulnerabilities Detected by testssl


You checked your site SSL configuration with testssl.sh (see Test Your SSL Configuration with testssl) and it returned some SSL vulnerabilities? Here are some recipes to help you make sense of it all. You will most likely need the Mozilla SSL Configuration Generator to protect your site with an up-to-date, correct SSL configuration.

Continue reading Resolve SSL Vulnerabilities Detected by testssl

How To Test SSL Configurations With testssl.sh


Is your current SSL Configuration secure enough? Is you https site rejecting old clients? Here comes a great tool called testssl.sh. It’s a bash script, developed by drwetter on Github, to test SSL Configurations

Enabling SSL for your site is a great idea overall. However, navigate around the multitude of SSL Configurations available for Apache and nginx is quite daunting. What’s best? What’s most secure? Are you privileging compatibility against security? testssl will help you decide what’s best for your site.

Continue reading How To Test SSL Configurations With testssl.sh

Meme Generator for Windows – Do It Yourself!


 

Meme Generator online are legion, and they do not offer the same options. Most will limit the size or shape of your meme, and most will apply their signature stamp on it. Plus it takes time to upload the image, refresh the page etc.

There is a solution for you though, and pretty easy believe me! It’s based on ImageMagick, with a simple drag&drop batch script for Windows. This solution will let you create classy memes even your mother would be proud of!

Continue reading Meme Generator for Windows – Do It Yourself!