Table of Contents

Winst32 Preprocessor

In short, this preprocessor is run instead of the original opsi WINST action processor as main action processor command to add additional control and logging features. During its own run it will decide if WINST is allowed to run or not and therefor control the overall action processing.

At the moment it is capable of:

But beware:

It is ABSOLUTELY NOT meant as a complete replacement for the allmighty winst32.exe that everybody knows or as a new installing mechanism!

THIS IS NOT FOR THE FAINT-HEARTED AND YOU SHOULD BE ABSOLUTELY SURE OF WHAT YOU ARE DOING! I WILL NOT GIVE ANY IN-DEPTH EXPLANATION FOR THE NEWBY USERS!

USE WITH CAUTION!USE WITH CAUTION!USE WITH CAUTION!NO RISK NO FUN!USE WITH CAUTION!USE WITH CAUTION!USE WITH CAUTION!

Links to the files:

v1.0, Tested with Windows 10 x64 1809 Enterprise — pandel 2019/10/25 13:52

v1.1, Tested with Windows 10 x64 1809 Enterprise — pandel 2019/10/28 15:00

Motivation

Let's imagine the following situation:

We have the problem that our company uses two different software deployment systems and the only one that we can control ourselfs is opsi. So it is rather important to know if we can safely process our opsi tasks. Additionally, we have some clients which run more or less important scheduled tasks which should not be disturbed, so we would have to check for that, too.

All these checks can be done via Windows registry queries, but not via opsi client agent directly. So I had the idea to write this preprocessor…

How it works

The preprocssor is run instead of the original winst32.exe via opsiclientd and takes control over the execution of winst32.exe based on registry checks itself. It acts as a wrapper to winst32.exe. Everything is controlled via a single configuration file.

You can create an example configuration file in the same directory by running winst32_preproc.exe without any command line parameter.

Where to install

That depends on your liking. You can put it into your standard opsi-client-agent installation folder, but then you have to take care of re-deployment after updating the client. This can be done by re-packaging the original opsi-client-agent-*.opsi with the included files, ie.

Modifications to opsiclientd.conf

In opsiclientd.conf you will find a configuration entry (or multiple ones if you configured your own/different events) looking like this:

[action_processor]
...
# Action processor command
command = "%action_processor.local_dir%\\%action_processor.filename%" /opsiservice "%service_url%" /clientid %global.host_id% /username %global.host_id% /password %global.opsi_host_key%

To get the preprocessor up and running, you have to replace the above mentioned command like so (notice the “-r” at the very end of the line!):

command = "%global.base_dir%\\winst32_preproc.exe" -f="%action_processor.local_dir%\\%action_processor.filename%" -s=%service_url% -c=%global.host_id% -u=%global.host_id% -p=%global.opsi_host_key% -l="%global.log_dir%\\winst32_preproc.log" -r

This example expects winst32_preproc.exe and its ini file in %global.base_dir% (see opsiclientd.conf). If you put it somewhere else, modify the line accordingly.

Command line parameters

The program contains a short help regarding the command line parameters. Simply issue “winst32_preproc.exe -h” to see

------------------------------------------------------------------------------------------------------------------
winst32_preproc (MIT licensed) v1.1.0.0 Command Line
 
Available command line options:
 
-f      --acp-command           Action processor filename, ie. winst32.exe
-s      --service_url           service URL
-c      --clientid              client id
-u      --username              service username
-p      --password              password
-d      --debug                 Debug output
-r      --run                   Production mode (REALLY activate the program!)
-l      --log                   Different logfile name
-h      --help                  Show this help
 
Default logfile: C:\Users\<your username>\AppData\Local\Temp\winst32_preproc\winst32_preproc.log

Standard mode = Test mode

For security reasons, the default mode of winst32_preproc ist test mode. That means, if you not explicitely specify production mode via command line, the program will only show you what would happen, but don't actually do anything. So, if you have configured the ini file according to your needs you can simple put both files in a directory on your test client and start the program from an administrator console to test it.

Let's assume the following (see opsiclientd.conf for your current values):

winst32_preproc.exe -d -t -f="C:\Program Files (x86)\opsi.org\opsi-client-agent\\opsi-winst\\winst32.exe" -s=https://192.168.10.10:4447/rpc -c=testclient.opsinet.local -u=testclient.opsinet.local -p=5f5efffffa26fe90473a734f1da68fed

NOTE: the path to winst32.exe must be the right one, otherwise he programm will not do anything.

The configuration: winst32_preproc.ini

The structure of the configuration file is rather simple. It consists of five blocks:

How the blocks work

registry_check

The keys and values you specify here will be checked for their existance AND values. They a treated as ONE single result, that means, if you specify multiple values to check for and one (ore more) values cannot be read or found, this check is considered “failed” and winst32.exe execution will be prevented.

process_check

The process names you specify here will be checked if they are running. They a treated as ONE single result, that means, if you specify multiple process names to check for and one (ore more) will found, this check is considered “failed” and winst32.exe execution will be prevented.

registry_pre / registry_post

The registry_pre and registry_post block work the same. They simply set the values you specify IF (AND ONLY IF) the registry_check block was passed successfully.

Configuration values

Registry checks

A configuration entry like

reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0

means, that (on a 64 bit machine!) the preprocessor looks into

HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV

for a value

FSVActive = 0

On a 64 bit machine, a line like

reg1=HKLM|32|SOFTWARE\RZ\FSV|FSVActive|0

would result in a actual registry key of

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\RZ\FSV

In general, a single line can be seen as:

reg<no>=<registry leaf>|<bitness>|<registry key>|<registry value>|<value type>|<value data>
Possible registry leaf values:
Possible bitnesses:
Possible value types:

Process checks

Process checks are much simpler:

A line like

proc1=winword.exe

will check, if winword.exe is found in the list of active processes.

Example

winst32_preproc.ini
[general]
version=1.1.0.0
logInstalled=1
logActions=1
 
[registry_check]
count=1
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0
 
[registry_pre]
count=2
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|1|REG_SZ
reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|1|REG_SZ
 
[registry_post]
count=3
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0|REG_SZ
reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|0|REG_SZ
reg3=HKLM|32|SOFTWARE\Somewhere\Else|SomeOtherValue|50|REG_DWORD
 
[process_check]
count=1
proc1=svchost.exe

The syntax should be obvious. “count” is simply the number of lines in the single block, every single entry starts with “reg” and a consecutive number. To deactivate a certain block completely, simply set its “count” value to 0. The single “reg” line entries will then be ignored, you don't have to delete them. DON'T delete a block to disable it!

What this example will do (in pseudo-lingo):

if ("FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" is "0") AND ("svchost.exe" is NOT in the list of active processes) then
	log currently installed products on client
	log current action requests
	set "FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" to "1"
	set "Active" under "HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\Winst" to "1"
	execute winst32.exe (with its parameters)
	set "FSVActive" under "HKEY_LOCAL_MACHINE\SOFTWARE\RZ\FSV" to "0"
	set "Active" under "HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\Winst" to "0"
	set "SomeOtherValue" under "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Somewhere\Else" to "50"
else
	exit and do nothing	

If you don't want the preprocessor to log your installed products and/or action requests everytime it is started, simply set the configuration values to 0.

Files

winst32_preproc.ini (example)

winst32_preproc.ini
[general]
version=1.1.0.0
logInstalled=1
logActions=1
 
[registry_check]
count=1
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0
 
[registry_pre]
count=2
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|1|REG_SZ
reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|1|REG_SZ
 
[registry_post]
count=3
reg1=HKLM|64|SOFTWARE\RZ\FSV|FSVActive|0|REG_SZ
reg2=HKLM|64|SOFTWARE\opsi.org\Winst|Active|0|REG_SZ
reg3=HKLM|32|SOFTWARE\Somewhere\Else|SomeOtherValue|50|REG_DWORD
 
[process_check]
count=1
proc1=svchost.exe

winst32_preproc.au3

winst32_preproc.au3
;*****************************************
;winst32_preproc.au3 by Holger Pandel
;Erstellt mit ISN AutoIt Studio v. 1.09
;*****************************************
 
;OPSI standard call to winst32
;command = "%action_processor.local_dir%\\%action_processor.filename%" /opsiservice "%service_url%" /clientid %global.host_id% /username %global.host_id% /password %global.opsi_host_key%
 
; modified preprocessor command
;_getopts()
;command = "%global.base_dir%\\winst32_preproc.exe" -f="%action_processor.local_dir%\\%action_processor.filename%" -s=%service_url% -c=%global.host_id% -u=%global.host_id% -p=%global.opsi_host_key% -l="%global.log_dir%\\winst32_preproc.log" -r
 
; includes
#include <Inet.au3>
#include <file.au3>
#include <array.au3>
#include <Constants.au3>
#include "UDFs\AssoArrays.au3"
#include "UDFs\GetOpt.au3"
#include "UDFs\json.au3"
 
Global Const $HTTP_STATUS_OK = 200
 
;Opt('MustDeclareVars', 1)
; appplication title and version
Global $header = "winst32_preproc (MIT licensed)"
Global $version = "1.1.0.0"
Global $logdir = @TempDir & "\winst32_preproc"
Global $log_mode = True ; write log file = true
Global $log_file = $logdir & "\winst32_preproc.log" ; set default logfile name
Global $opsi_command = "C:\Program Files (x86)\opsi.org\opsi-client-agent\opsi-winst\winst32.exe"
Global $opsi_url = "https://server.id:4447/rpc"
Global $opsi_client =  "localhost.localdom.net"
Global $opsi_user = "localhost.localdom.net"
Global $opsi_pass = "1234567890"
Global $Pid, $Handle, $ExitCode, $Cmd,  $Cmdout
Global $iniName = "winst32_preproc.ini"
Global $isRegOk =  False
 
; if true, it outputs messages on console, if false onbly to log file
Global $debug = False
; if true, do not actually run any command
Global $test = True
 
Local $msg = '------------------------------------------------------------------------------------------------------------------' & @CRLF ; start line
$msg &= $header & ' v' & $version & ' Command Line' & @CRLF & @CRLF ; Message.
 
; Load ini data
Global $iniName = "winst32_preproc"
_getIniValues($iniName)
 
; let's have a look at the CmdLine
If $CmdLine[0] = 0 Then
	$msg &= "No arguments passed. See -h for help or use -d for a test run. Exiting."  & @CRLF & @CRLF
	ConsoleWrite($msg)
	Exit 0
Else 
	_debug($msg)
EndIf
 
; get cmdline options
_getopts()
 
If not FileExists($opsi_command) Then
	_debug("OPSI action processor not found. Exit.")
	_exit(1)
EndIf
 
_debug("Running as user: " & @UserName)
_debug("Profile dir    : " & @UserProfileDir)
_debug("Log file       : " & $log_file)
 
; for debugging purpose
;If $test Then 
;	x_display()
;EndIf
 
; ---------------------------------------------------------------------------------------------------------------------
; ---- MAIN ---------
 
If x($iniName & ".registry_check.count") > 0 Then
	Local $isRegOk = _runRegistryCheck()
	If $isRegOk Then 
		_debug("All registry checks passed successfully.")
	Else
		_debug("One or more registry checks where unsuccsessful.")
		_exit(0)
	EndIf
EndIf
 
If x($iniName & ".process_check.count") > 0 Then
	Local $isProcOk = _runProcessCheck()
	If $isProcOk Then 
		_debug("All process checks passed successfully.")
	Else
		_debug("One or more process checks where unsuccsessful.")
		_exit(0)
	EndIf
EndIf
 
 
if $test Then
	; test run
	$Cmdout = '"' & $opsi_command & '" /opsiservice "' & $opsi_url & '" /clientid ' & $opsi_client & ' /username ' & $opsi_user & ' /password ***(confidential)***'
	_debug("")
	_debug("TESTMODE processing")
	_debug("-------------------")
	_debug("action processor: " &  $opsi_command)
	_debug("url:              " &  $opsi_url)
	_debug("client:           " &  $opsi_client)
	_debug("user:             " &  $opsi_user)
	_debug("pass:             (CONFIDENTIAL)")
	_debug("")
	If x($iniName & ".general.logInstalled") = 1 Then _opsi_showInstalledProducts($opsi_url, $opsi_client, $opsi_user, $opsi_pass)
	If x($iniName & ".general.logActions") = 1 Then _opsi_showActionRequests($opsi_url, $opsi_client, $opsi_user, $opsi_pass)
	_debug("")
	_debug("I)   Registry PRE-phase:")
	_updateRegistry("pre")	
	_debug("")
	_debug("II)  Command to be launched: " &  $Cmdout)
	_debug("")
	_debug("III) Registry POST-phase:")
	_updateRegistry("post")	
	_debug("")
	_debug("Testing only, no opsi processing for now.")
	_debug("For production mode, use the -r switch (USE WITH CAUTION).")
	_exit(0)
Else
	; production run
	If x($iniName & ".general.logInstalled") = 1 Then _opsi_showInstalledProducts($opsi_url, $opsi_client, $opsi_user, $opsi_pass)
	If x($iniName & ".general.logActions") = 1 Then _opsi_showActionRequests($opsi_url, $opsi_client, $opsi_user, $opsi_pass)
	_updateRegistry("pre")	
	$retval = _startActionProcessor($opsi_command, $opsi_url, $opsi_user, $opsi_client, $opsi_pass)
	_updateRegistry("post")
 
	_exit($retval)
EndIf
 
; ---------------------------------------------------------------------------------------------------------------------
; ---- SUPPORT FUNCTIONS ---------
 
; check if registry values are as necessary
Func _runRegistryCheck()
	Local $rc =  False
	_debug("Running registry checkss...")
 
	If x($iniName & ".registry_check.count") = 0 Then
		_debug("--> nothing to check")
		return True
	EndIf
 
	; Example entry: reg1=HKLM|64|SOFTWARE\FIDUCIA\Bitlocker|FSVActive|0
 
	For $i = 1 To x($iniName & ".registry_check.count")
		Local $a = x($iniName & ".registry_check.reg" & $i)
		_debug("Test " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & "=" & $a[4])
		If $a[2] = "32" Then 
			If RegRead($a[0] & "\" & $a[2], $a[3]) = $a[4] then 
				$rc = True
				_debug("--> rc=" & $rc)
			Else
				$rc = False
				_debug("--> rc=" & $rc)
				return $rc
			EndIf
		Else 
			If RegRead($a[0] & $a[1] & "\" & $a[2], $a[3]) = $a[4] then 
				$rc = True
				_debug("--> rc=" & $rc)
			Else
				$rc = False
				_debug("--> rc=" & $rc)
				return $rc
			EndIf
		EndIf
	Next
 
	return $rc
EndFunc
 
; check if specified process exist
Func _runProcessCheck()
	Local $rc = False
	_debug("Running process checks...")
 
	If x($iniName & ".process_check.count") = 0 Then
		_debug("--> nothing to check")
		return True
	EndIf
 
	; Example entry: proc1=winword.exe
	For $i = 1 To x($iniName & ".process_check.count")
		Local $a = x($iniName & ".process_check.proc" & $i)
		_debug("Test if not running " & $i & ": " & $a)
		Local $pid = ProcessExists($a)
		If $pid > 0 Then
			$rc =  False
			_debug("--> rc=" & $rc)
			_debug("--> PID=" & $pid)
			return $rc
		Else
			$rc = True
			_debug("--> rc=" & $rc)
			return $rc			
		EndIf
	Next
EndFunc
 
; set registry entries 
; $phase can be "pre" or "post"
; RegWrite ( "keyname" [, "valuename", "type", value] )
Func _updateRegistry($phase)
	Local $rc =  False
	_debug("Running registry phase: " & $phase)
 
	; Example entry: reg1=HKLM|32|SOFTWARE\FIDUCIA\Bitlocker|FSVActive|0|REG_SZ	
 
	If x($iniName & ".registry_" & $phase & ".count") = 0 Then
		_debug("--> nothing to do in this phase")
		return
	EndIf
 
	For $i = 1 To x($iniName & ".registry_" & $phase & ".count")
		Local $a = x($iniName & ".registry_" &  $phase & ".reg" & $i)
		If not $test Then
			_debug("Setting " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & "=" & $a[4])
			If $a[2] = "32" Then 
				If RegWrite($a[0] & "\" & $a[2], $a[3], $a[5], $a[4]) then 
					$rc = True
					_debug("--> rc=" & $rc)
				EndIf
			Else 
				If RegWrite($a[0] & $a[1] & "\" & $a[2], $a[3], $a[5], $a[4]) then 
					$rc = True
					_debug("--> rc=" & $rc)
				EndIf
			EndIf
		Else
			_debug("(TESTMODE) Setting " & $i & ": " & $a[0] & "\" & $a[2] & "(" & $a[1] & "bit):" & $a[3] & " = " & $a[5] & ":" & $a[4])
		EndIf
	Next
EndFunc
 
; run action processor command
Func _startActionProcessor($command, $URL, $clientid, $user, $pass)
	_debug("Starting action...")
 
	$Cmd =  '"'   & $command & '" /opsiservice "' & $URL & '" /clientid ' & $clientid & ' /username ' & $user & ' /password ' & $pass
	$Cmdout = '"' & $command & '" /opsiservice "' & $URL & '" /clientid ' & $clientid & ' /username ' & $user & ' /password ***(confidential)***'
	_debug("action processor: " &  $command)
	_debug("url:              " &  $URL)
	_debug("client:           " &  $clientid)
	_debug("user:             " &  $user)
	_debug("pass:             (CONFIDENTIAL)")
	_debug("cmd:              " &  $Cmdout)
 
	$Pid = Run($Cmd, '', @SW_SHOW, $STDOUT_CHILD + $STDERR_CHILD)
	$Handle = _ProcessExitCode($Pid)
	ProcessWaitClose($pid)
	$ExitCode = _ProcessExitCode($Pid, $Handle)
	_ProcessCloseHandle($Handle)
 
	return $ExitCode
EndFunc
 
; handle command line options
Func _getopts()
	Local $sMsg = '------------------------------------------------------------------------------------------------------------------' & @CRLF ; start line
	$sMsg &= $header & ' v' & $version & ' Command Line' & @CRLF & @CRLF ; Message.
	Local $sOpt, $sSubOpt, $sOper
	Local $h_specified = False
	Local $failure = False
	; Options array, entries have the format [short, long, default value]
	Local $aOpts[9][3] = [ _
			['-f', '--acp_command', $GETOPT_REQUIRED_ARGUMENT], _
			['-s', '--service_url', $GETOPT_REQUIRED_ARGUMENT], _
			['-c', '--clientid', $GETOPT_REQUIRED_ARGUMENT], _
			['-u', '--username', $GETOPT_REQUIRED_ARGUMENT], _
			['-p', '--password', $GETOPT_REQUIRED_ARGUMENT], _
			['-d', '--debug', True], _
			['-r', '--r', True], _
			['-l', '--log', $log_file], _
			['-h', '--help', True]]
 
	_GetOpt_Set($aOpts) ; Set options.
 
	If 0 < $GetOpt_Opts[0] Then ; If there are any options...
 
		; first loop: analyse parameters and create message texts, exit program if needed
		While 1 ; ...loop through them one by one.
			; Get the next option passing a string with valid options.
			$sOpt = _GetOpt('f:s:c:u:p:rdtlh') ; p: means -p option requires an argument.
			If Not $sOpt Then ExitLoop ; No options or end of loop.
			; Check @extended above if you want better error handling.
			; The current option is stored in $GetOpt_Opt, it's index (in $GetOpt_Opts)
			; in $GetOpt_Ind and it's value in $GetOpt_Arg.
			Switch $sOpt ; What is the current option?
				Case '?' ; Unknown options come here. @extended is set to $E_GETOPT_UNKNOWN_OPTION
					$sMsg &= 'Unknown option: ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ').' & @CRLF
					$sMsg &= 'UNKNOWN OPTION passed.' & @CRLF
					$failure = True
				Case ':' ; Options with missing required arguments come here. @extended is set to $E_GETOPT_MISSING_ARGUMENT
					$sMsg &= 'MISSING REQUIRED ARGUMENT for option: ' & $GetOpt_Ind & ': ' & $GetOpt_Opt & @CRLF
					$failure = True
				Case 'l'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$log_file = $GetOpt_Arg
				Case 'd'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$debug = True
				Case 'r'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$test = False
				Case 'f'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$opsi_command = $GetOpt_Arg
				Case 's'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$opsi_url = $GetOpt_Arg
				Case 'c'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$opsi_client = $GetOpt_Arg
				Case 'u'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with required value "' & $GetOpt_Arg & '" (' & VarGetType($GetOpt_Arg) & ')'
					$sMsg &= '.' & @CRLF
					$opsi_user = $GetOpt_Arg
				Case 'p'
					$sMsg &= 'Option ' & $GetOpt_Ind & ': ' & $GetOpt_Opt
					$sMsg &= ' with ***(confidential)*** value supplied. '
					$sMsg &= '.' & @CRLF
					$opsi_pass = $GetOpt_Arg
 
				Case 'h'
					$sMsg &= 'Available command line options:' & @CRLF & @CRLF & _
					'-f' & @TAB & '--acp-command   ' & @TAB & 'Action processor filename, ie. winst32.exe' & @CRLF & _
					'-s' & @TAB & '--service_url   ' & @TAB & 'service URL' & @CRLF & _
					'-c' & @TAB & '--clientid      ' & @TAB & 'client id' & @CRLF & _
					'-u' & @TAB & '--username      ' & @TAB & 'service username' & @CRLF & _
					'-p' & @TAB & '--password      ' & @TAB & 'password' & @CRLF & _
					'-d' & @TAB & '--debug         ' & @TAB & 'Debug output' & @CRLF & _
					'-r' & @TAB & '--run           ' & @TAB & 'Production mode (REALLY activate the program!)' & @CRLF & _
					'-l' & @TAB & '--log           ' & @TAB & 'Different logfile name' & @CRLF & _
					'-h' & @TAB & '--help          ' & @TAB & 'Show this help' & @CRLF & @CRLF & _ 
					'Default logfile: ' & $log_file & @CRLF & @CRLF
					$h_specified = True
			EndSwitch
		Wend
 
		If 0 < $GetOpt_Opers[0] Then ; If there are any operands...
			$sMsg &= 'INVALID OPERANDS PASSED. This is not allowed. Check for missing ''='' in --path or --log option!' & @CRLF
			While 1 ; ...loop through them one by one.
				$sOper = _GetOpt_Oper() ; Get the next operand.
				If Not $sOper Then ExitLoop ; no operands or end of loop.
				; Check @extended above if you want better error handling.
				$sMsg &= 'Operand ' & $GetOpt_OperInd & ': ' & $sOper & @CRLF
			WEnd
			$failure = True
		EndIf
 
		If $failure Then $sMsg &= @CRLF & 'Please check the command line parameters!' & @CRLF
 
		If $h_specified Or $failure Then ; show help screen or wrong options ?
			ConsoleWrite(@crlf & $sMsg)
			If $failure Then _exit(1)
			_exit(0)
		EndIf
	EndIf
 
	; write header to log
	_writeLog(_formatIOText($sMsg))
EndFunc
 
; part of _getIniValues
Func _getLeaf($leaf, $tab)
	Local $str
	If not IsObj($leaf) Then Return
	$colKeys = $leaf.Keys
	For $strKey in $colKeys
		$str = ""
		If IsArray($leaf.Item($strKey)) Then		
			$str = $leaf.Item($strKey)[0]
			For $i = 1 to Ubound($leaf.Item($strKey)) - 1
				$str &= "|" & $leaf.Item($strKey)[$i]
			Next
			_debug($tab & $strKey & ' -> ' & $str)
		Else
			_debug($tab & $strKey & ' -> ' & $leaf.Item($strKey))
		EndIf
		_getLeaf($leaf.Item($strKey), $tab & "  ")
	Next				
EndFunc
 
; read ini values
Func _getIniValues($iniName)
	Dim $szDrive, $szDir, $szFName, $szExt, $pathTemp, $kdSel, $op
	$pathTemp = _PathSplit(@ScriptFullPath, $szDrive, $szDir, $szFName, $szExt)
 
	Dim $appPath = $szDrive & $szDir
	Dim $iniFile = $appPath & $iniName &  ".ini"
 
	Dim $rc, $cpm[3], $msg, $str, $colKeys
 
	_debug("Ini file       : " & $iniFile)
 
	If FileExists($iniFile) Then ;--- global INI file found
		$rc = _ReadAssocFromIni2($iniFile)
		If @error Then
			_debug("Error while reading ini file. Errno: " & @error)
		Else
			_debug("Entries read: " & $rc)
			_debug("Logging configuration:")
			_debug("--------------- snip ------------------")
			_getLeaf($_xHashCollection, "  ")
			_debug("--------------- snip -----------------")
			_debug("INI reading complete.")
		EndIf
 
	Else ;--- INI not existing, creating new one in script directory
		$msg = "INI file not found: " & $iniFile & @CRLF & _
				"A new INI file with example values will be created in the script directory." & @CRLF & _
				"Please customize the INI file before using it." & @CRLF & @CRLF
		ConsoleWrite($msg)
		_createIni($appPath & $iniName)
 
	EndIf
 
EndFunc   ;==>_getIniValues
 
; get current product status
Func _REST_request($req, $clientid, $user, $pass)
	Local $REST = ObjCreate("WinHttp.WinHttpRequest.5.1")
	Local $agent ='Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36'
 
	; https://$serverid:$serverport/rpc?{ "id": 1, "method": "getProductInstallationStatus_listOfHashes", "params": ["$clientid"] }
	;ConsoleWrite(@CRLF & "Request: " & $req)
 
	$REST.Open("GET", $req, False)
	If (@error) Then Return SetError(1, 0, "Error on open")
 
	$REST.SetRequestHeader('Authorization','Basic ' & base64($user & ":" & $pass,  True,  True))
	$REST.setRequestHeader ("User-Agent", $agent)
	$REST.Option(4) = 13056	
 
	$REST.Send()
	If (@error) Then Return SetError(2, 0, "Error on send")
 
	If ($REST.Status <> $HTTP_STATUS_OK) Then Return SetError(3, 0, "Error on request - HTTP status " & $REST.Status)
 
	return $REST.ResponseText
EndFunc 
 
; convert lastStateChange into more readable format
Func _opsiDate($raw)
	Local $year = StringMid($raw,1, 4)
	Local $mon = StringMid($raw,5, 2)
	Local $day = StringMid($raw,7, 2)
	Local $hour = StringMid($raw,9, 2)
	Local $min = StringMid($raw,11, 2)
	return $day & "." & $mon & "." & $year & " " & $hour & ":" & $min
EndFunc
 
; request all installed products on client
Func _opsi_showInstalledProducts($URL, $clientid, $user, $pass)
	Local $_getProductInstallationStatus_listOfHashes = $URL & '?{ "id": 1, "method": "getProductInstallationStatus_listOfHashes", "params": ["' & $clientid & '"] }'
	Local $retval = _REST_request($_getProductInstallationStatus_listOfHashes, $clientid, $user, $pass)
	If (@error) Then 
		_debug("Error on REST request: " & $retval)
		return
	EndIf
 
	Local $data = json_decode($retVal)
	;json_dump($retval)
 
	_debug("Currently installed:")
 
	Local $i = 0
	While 1
		$product_id = json_get($data, '.result[' & $i & '].productId')
		$installationStatus = json_get($data, '.result[' & $i & '].installationStatus')
		$installed = json_get($data, '.result[' & $i & '].lastStateChange')
 
		If @error Then ExitLoop
		If $installationStatus = "installed" Then
			_debug("--> " & $product_id & " (lastStateChange: " & _opsiDate($installed) &  ")")
		EndIf
		$i += 1
	WEnd
EndFunc
 
; request current action requests for client
Func _opsi_showActionRequests($URL, $clientid, $user, $pass)
	Local $_getProductActionRequests_listOfHashes = $URL & '?{ "id": 1, "method": "getProductActionRequests_listOfHashes", "params": ["' & $clientid & '"] }'
	Local $retval = _REST_request($_getProductActionRequests_listOfHashes, $clientid, $user, $pass)
	If (@error) Then 
		_debug("Error on REST request: " & $retval)
		return
	EndIf
 
	Local $data = json_decode($retVal)
	;json_dump($retval)
 
	_debug("Current action requests:")
 
	Local $i = 0
	While 1
		$product_id = json_get($data, '.result[' & $i & '].productId')
		$actionRequest = json_get($data, '.result[' & $i & '].actionRequest')
 
		If @error Then ExitLoop
		If $actionRequest <> "none" Then
			_debug("--> " & $product_id & ": " & $actionRequest)
		EndIf
		$i += 1
	WEnd
EndFunc
 
; common exit 
Func _exit($rc)
	_debug("")
	_debug("winst32_preproc exiting.")
	_debug("------------------------------------------------------------------------------------------------------------------")
	Exit $rc
EndFunc
 
; create default example ini file
Func _createIni($ini)
 
		x($ini & ".general.version", $version)		
		x($ini & ".general.logInstalled", 1)
		x($ini & ".general.logActions", 1)
 
		x($ini & ".registry_check.count", 1)
 
		Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "0"]
		x($ini & ".registry_check.reg1", $a)
 
		x($ini & ".registry_pre.count", 2)
 
		Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "1", "REG_SZ"]
		x($ini & ".registry_pre.reg1", $a)
		Local $a = ["HKLM", "64", "SOFTWARE\opsi.org\Winst", "Active", "1", "REG_SZ"]
		x($ini & ".registry_pre.reg2", $a)
 
		x($ini & ".registry_post.count", 3)
 
		Local $a = ["HKLM", "64", "SOFTWARE\RZ\FSV", "FSVActive", "0", "REG_SZ"]
		x($ini & ".registry_post.reg1", $a)
		Local $a = ["HKLM", "64", "SOFTWARE\opsi.org\Winst", "Active", "0", "REG_SZ"]
		x($ini & ".registry_post.reg2", $a)
		Local $a = ["HKLM", "32", "SOFTWARE\Somewhere\Else|SomeOtherValue", "50", "REG_DWORD"]
		x($ini & ".registry_post.reg3", $a)
 
		x($ini & ".process_check.count", 1)
 
		Local $a = ["svchost.exe"]
		x($ini & ".process_check.proc1", $a)
 
		_WriteAssocToIni($ini)
EndFunc
 
; ---------------------------------------------------------------------------------------------------------------------
; ---- TOOLS ---------
 
; output debug messages to console
Func _debug($dbg, $line = @ScriptLineNumber)
	Local $time = "[" & @MDAY & "." & @MON & "." & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & "] ! DEBUG"
	If $debug Then 
		if $line = -1 then
			ConsoleWrite(@CRLF & $time & ": " & $dbg)
		Else 
			ConsoleWrite(@CRLF & $time & " (" & $line & "): " & $dbg)
		Endif
	EndIf
	_writeLog(_formatIOText($dbg))
EndFunc   ;==>_debug
 
; format message text line for console and log
; if $mark is "?????", then special question format is used
Func _formatIOText($text, $mark = "")
	Local $time = "[" & @MDAY & "." & @MON & "." & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & "] "
 
	If $mark = "" Then
		$text = $time & StringReplace($text, @CRLF, @CRLF & $time)
	ElseIf $mark = "?????" Then
		$text = $time & $text & ": "
	Else
		$text = $time & "! " & $mark & ": " & StringReplace($text, @CRLF, @CRLF & $time & "! " & $mark & ": ")
	EndIf
 
	Return $text
EndFunc   ;==>_formatIOText
 
; log messages to disc
Func _writeLog($msg, $nocrlf = False)
	; write lines to logfile, even if --quiet is specified
	If $log_mode Then
		Local $file = FileOpen($log_file, 1 + 8) ; create dir and open/create file
		If $nocrlf Then
			FileWrite($file, $msg)
		Else
			FileWrite($file, @CRLF & $msg)
		EndIf
		FileFlush($file)
		FileClose($file)
	EndIf
EndFunc   ;==>_writeLog
 
 
;===============================================================================
;
; Function Name:    _ProcessExitCode()
; Description:      Returns a handle/exitcode from use of Run().
; Parameter(s):     $i_Pid        - ProcessID returned from a Run() execution
;                   $h_Process    - Process handle
; Requirement(s):   None
; Return Value(s):  On Success - Returns Process handle while Run() is executing
;                                (use above directly after Run() line with only PID parameter)
;                              - Returns Process Exitcode when Process does not exist
;                                (use above with PID and Process Handle parameter returned from first UDF call)
;                   On Failure - 0
; Author(s):        MHz (Thanks to DaveF for posting these DllCalls in Support Forum)
;
;===============================================================================
;
Func _ProcessExitCode($i_Pid, $h_Process = 0)
	; 0 = Return Process Handle of PID else use Handle to Return Exitcode of a PID
	Local $v_Placeholder
	If Not IsArray($h_Process) Then
		; Return the process handle of a PID
		$h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $i_Pid)
		If Not @error Then Return $h_Process
	Else
		; Return Process Exitcode of PID
		$h_Process = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', $v_Placeholder)
		If Not @error Then Return $h_Process[2]
	EndIf
	Return 0
EndFunc   ;==>_ProcessExitCode
 
; close handle to attached process
Func _ProcessCloseHandle($h_Process)
	; Close the process handle of a PID
	DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process)
	If Not @error Then Return 1
	Return 0
EndFunc   ;==>_ProcessCloseHandle
 
; Get STDOUT and ERROUT from commandline tool
Func ShowStdOutErr($l_Handle, $ShowConsole = 1, $Replace = "", $ReplaceWith = "")
	Local $Line = "x", $Line2 = "x", $tot_out, $err1 = 0, $err2 = 0, $cnt1 = 0, $cnt2 = 0
 
	Do
		Sleep(10)
		$Line = StdoutRead($l_Handle)
		$err1 = @error
		If $Replace <> "" Then $Line = StringReplace($Line, $Replace, $ReplaceWith)
		$tot_out &= $Line
		If $ShowConsole Then
			ConsoleWrite($Line)
		EndIf
 
		$Line2 = StderrRead($l_Handle)
		$err2 = @error
		If $Replace <> "" Then $Line2 = StringReplace($Line2, $Replace, $ReplaceWith)
		$tot_out &= $Line2
		If $ShowConsole Then
			ConsoleWrite($Line2)
		EndIf
 
		; end the loop also when AutoIt3 has ended but a sub process was shelled with Run() that is still active
		; only do this every 50 cycles to avoid cpu hunger
		If $cnt1 = 50 Then
			$cnt1 = 0
			; loop another 50 times just to ensure the buffers emptied.
			If Not ProcessExists($l_Handle) Then
				If $cnt2 > 2 Then ExitLoop
				$cnt2 += 1
			EndIf
		EndIf
		$cnt1 += 1
	Until ($err1 And $err2)
	Return $tot_out
EndFunc   ;==>ShowStdOutErr
 
; Author: MilesAhead
; http://www.autoitscript.com/forum/topic/110768-itaskbarlist3/page__view__findpost__p__910631
; read AssocArray from IniFile Section
; returns number of items read - sets @error on failure
; modified (original see AssoArray.au3): Holger Pandel, 2015: accept full path name of ini
Func _ReadAssocFromIni2($myIni = 'config.ini', $mySection = '', $sSep = "|")
	Local $szDrive, $szDir, $szFName, $szExt, $pathTemp
 
	$pathTemp = _PathSplit(@ScriptFullPath, $szDrive, $szDir, $szFName, $szExt)
 
	$sIni = $szFName
 
    If $mySection == '' Then
        $aSection = IniReadSectionNames ($myIni); All sections
        If @error Then 
			_debug("Error while reading section names from: " & $myIni)
			Return SetError(@error, 0, 0)
		EndIf
    Else
        Dim $aSection[2] = [1,$mySection]; specific Section
    EndIf
 
    For $i = 1 To UBound($aSection)-1
 
        Local $sectionArray = IniReadSection($myIni, $aSection[$i])
        If @error Then
			_debug("Error while reading section: " & $aSection[$i])
			Return SetError(1, 0, 0)
		EndIf
        For $x = 1 To $sectionArray[0][0]
            If StringInStr($sectionArray[$x][1], $sSep) then
                $posS = _MakePosArray($sectionArray[$x][1], $sSep)
            Else
                $posS = $sectionArray[$x][1]
            EndIf
            x($sIni&"."&$aSection[$i]&"."&$sectionArray[$x][0], $posS)
        Next
 
    next
    Return $sectionArray[0][0]
EndFunc   ;==>_ReadAssocFromIni
 
;==============================================================================================================================
; Function:         base64($vCode [, $bEncode = True [, $bUrl = False]])
;
; Description:      Decode or Encode $vData using Microsoft.XMLDOM to Base64Binary or Base64Url.
;                   IMPORTANT! Encoded base64url is without @LF after 72 lines. Some websites may require this.
;
; Parameter(s):     $vData      - string or integer | Data to encode or decode.
;                   $bEncode    - boolean           | True - encode, False - decode.
;                   $bUrl       - boolean           | True - output is will decoded or encoded using base64url shema.
;
; Return Value(s):  On Success - Returns output data
;                   On Failure - Returns 1 - Failed to create object.
;
; Author (s):       (Ghads on Wordpress.com), Ascer
;===============================================================================================================================
Func base64($vCode, $bEncode = True, $bUrl = False)
 
    Local $oDM = ObjCreate("Microsoft.XMLDOM")
    If Not IsObj($oDM) Then Return SetError(1, 0, 1)
 
    Local $oEL = $oDM.createElement("Tmp")
    $oEL.DataType = "bin.base64"
 
    If $bEncode then
        $oEL.NodeTypedValue = Binary($vCode)
        If Not $bUrl Then Return $oEL.Text
        Return StringReplace(StringReplace(StringReplace($oEL.Text, "+", "-"),"/", "_"), @LF, "")
    Else
        If $bUrl Then $vCode = StringReplace(StringReplace($vCode, "-", "+"), "_", "/")
        $oEL.Text = $vCode
        Return $oEL.NodeTypedValue
    EndIf
 
EndFunc ;==>base64

AssoArrays.au3

AssoArrays.au3
#include-once
#include <GUIConstantsEx.au3>
; #INDEX# =======================================================================================================================
; Title .........: xHashCollection
; AutoIt Version : 3.3.4.0
; Language ......: English
; Description ...: Create and use Multidimentional Associative arrays
; Author ........: OHB <me at orangehairedboy dot com>
; ===============================================================================================================================
;~ $tt = TimerInit()
Global $_xHashCollection = ObjCreate( "Scripting.Dictionary" ), $_xHashCache
;~ ConsoleWrite("Asso Array Load up time: "&Round(TimerDiff($tt) ) & @CRLF)
; #FUNCTION# ====================================================================================================================
; Name...........: x
; Description ...: Gets or sets a value in an Associative Array
; Syntax.........: SET: x( $sKey , $vValue )
;                 GET: x( $key )
; Parameters ....: $sKey - the key to set or get. Examples:
;                 x( 'foo' )           gets value of foo
;                 x( 'foo.bar' )       gets value of bar which is a key of foo
;                 $bar = "baz"
;                 x( 'foo.$bar' )     gets value of baz which is a key of foo (variables are expanded)
; Return values .: Success - When setting, return the value set. When getting, returns the requested value.
;                 Failure - Returns a 0
; Author ........: OHB <me at orangehairedboy dot com>
; ===============================================================================================================================
Func x( $sKey = '' , $vValue = '' )
    $func = "get"
    If @NumParams <> 1 Then $func = "set"
    If $sKey == '' Then
        If $func == "get" Then
            Return $_xHashCollection
        Else
            $_xHashCollection.removeAll
            Return ''
        EndIf
    EndIf
    $parts = StringSplit( $sKey , "." )
    $last_key = $parts[$parts[0]]
    $cur = $_xHashCollection
    For $x = 1 To $parts[0] - 1
        If Not $cur.exists( $parts[$x] ) Then
            If $func == "get" Then Return
            $cur.add( $parts[$x] , ObjCreate( "Scripting.Dictionary" ) )
        EndIf
        $cur = $cur.item( $parts[$x] )
    Next
    If IsPtr( $vValue ) Then $vValue = String( $vValue )
    If $func == "get" Then
        If Not $cur.exists( $last_key ) Then Return
        $item = $cur.item( $last_key )
        Return $item
    ElseIf Not $cur.exists( $last_key ) Then
        $cur.add( $last_key , $vValue )
    Else
        $cur.item( $last_key ) = $vValue
    EndIf
    Return $vValue
EndFunc
 
; #FUNCTION# ====================================================================================================================
; Name...........: x_del
; Description ...: Removes a key from an Associative Array
; Syntax.........: x_del( $sKey )
; Parameters ....: $sKey - the key to remove.
; Return values .: Success - True
;                 Failure - False
; Author ........: OHB <me at orangehairedboy dot com>
; ===============================================================================================================================
Func x_del( $sKey )
    If $sKey == '' Then Return x( '' , '' )
    $parts = StringSplit( $sKey , "." )
    $cur = $_xHashCollection
    For $x = 1 To $parts[0] - 1
        If Not $cur.exists( $parts[$x] ) Then Return False
        $cur = $cur.item( $parts[$x] )
    Next
    $cur.remove( $parts[$parts[0]] )
    Return True
EndFunc
 
; #FUNCTION# ====================================================================================================================
; Name...........: x_display
; Description ...: Displays the contents of an Associative Array
; Syntax.........: x_display( $sKey )
; Parameters ....: $sKey - the key to display. Examples:
;                 x_display()         displays everything
;                 x_display( 'foo' )   displays the contents of foo
; Author ........: OHB <me at orangehairedboy dot com>
; ===============================================================================================================================
Func x_display( $key = '' )
    $text = $key
    If $key <> '' Then $text &= " "
    $text &= StringTrimRight( _x_display( x( $key ) , '' ) , 2 )
    ConsoleWrite($text & @LF)
    $wHnd = GUICreate( "Array " & $key , 700 , 500 )
    GUISetState( @SW_SHOW , $wHnd )
    $block = GUICtrlCreateEdit( $text , 5 , 5 , 690 , 490 )
    GUICtrlSetFont( $block , 10 , 400 , -1 , 'Courier' )
    While 1
        If GUIGetMsg() == -3 Then ExitLoop
    WEnd
    GUISetState( @SW_HIDE , $wHnd )
    GUIDelete( $wHnd )
EndFunc
 
; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: _x_display
; Description ...: Itterates through an array and builds output for x_display
; Author ........: OHB <me at orangehairedboy dot com>
; ===============================================================================================================================
Func _x_display( $item , $tab )
    If IsObj( $item ) Then
        $text = 'Array (' & @CRLF
        $itemAdded = False
        For $i In $item
            $text &= $tab & "  [" & $i & "] => " & _x_display( $item.item($i) , $tab & "  " )
            $itemAdded = True
        Next
        If Not $itemAdded Then $text &= @CRLF
        $text &= $tab & ')'
    ElseIf IsArray( $item ) Then
        $text = "Array"
        $totalItems = 1
        $dimensions = UBound( $item , 0 )
        For $dimension = 1 To $dimensions
            $size = UBound( $item , $dimension )
            $totalItems *= $size
            $text &= "[" & $size & "]"
        Next
        $text &= " (" & @CRLF
        For $itemID = 0 To $totalItems - 1
            $idName = ''
            $idNum = $itemID
            For $dimension = 1 To $dimensions - 1
                $mul = ( $totalItems / UBound( $item , $dimension ) )
                $a = Floor( $idNum / $mul )
                $idName &= '[' & $a & ']'
                $idNum -= ( $a * $mul )
            Next
            $idName &= '[' & $idNum & ']'
            $text &= $tab & "  " & $idName & " => " & _x_display( Execute( "$item" & $idName ) , $tab & "  " )
        Next
        $text &= $tab & ")"
    Else
        $text = $item
    EndIf
    $text &= @CRLF
    Return $text
EndFunc
 
 
; Name...........: x_count
; Description ...: Returns a count of items
; Syntax.........: x_count( $sKey )
; Parameters ....: $sKey - the key
; Return values .: Success: count of items of a provided key
;                          x_count() returns count os keys on first level
;                 Failure: 0, set error to:
;                               -1 = the key is not an object, but an item with a value
;                               -2 = the key does not exist
; Author ........:  shEiD (original "x" UDF by: OHB)
Func x_count($sKey = '')
    If $sKey == '' Then Return $_xHashCollection.Count
    Local $cur = $_xHashCollection
    Local $parts = StringSplit($sKey, ".")
    For $x = 1 To $parts[0]
        If Not $cur.exists($parts[$x]) Then Return SetError(-2, 0, 0)
        $cur = $cur.item($parts[$x])
    Next
    If Not IsObj($cur) Then Return SetError(-1, 0, 0)
    Return $cur.Count
EndFunc   ;==>x_count
 
 
; Author: MilesAhead
; http://www.autoitscript.com/forum/topic/110768-itaskbarlist3/page__view__findpost__p__910631
;write AssocArray to IniFile Section
;returns 1 on success - sets @error on failure
Func _WriteAssocToIni($myIni = 'config', $mySection = '', $bEraseAll = false, $sSep = "|")
    $myIni = $myIni&".ini"
    If $bEraseAll then
        $temp = FileOpen($myIni, 2)
        FileClose($temp)
    EndIf
 
    Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1)
 
    If Not $_xHashCollection.Exists($sIni) Then Return SetError(1, 0, 0)
 
    If $mySection == '' Then
        $aSection = $_xHashCollection($sIni).Keys(); All sections
    Else
        Dim $aSection[1] = [$mySection]; specific Section
    EndIf
    Local $retVal = 0
    For $i = 0 To UBound($aSection)-1
        $cur = x($sIni&"."&$aSection[$i])
        $retVal = 0
        If $cur.Count() < 1 Then
            Return SetError(1, 0, 0)
        EndIf
        Local $iArray[$cur.Count()][2]
        Local $aArray = $cur.Keys()
        For $x = 0 To UBound($aArray) - 1
            $iArray[$x][0] = $aArray[$x]
            $value = x($sIni&"."&$aSection[$i]&"."&$aArray[$x])
            If IsArray($value) then
                $iArray[$x][1] = _MakePosString($value, $sSep)
            Else
                $iArray[$x][1] = $value
            EndIf
        Next
        $retVal = IniWriteSection($myIni, $aSection[$i], $iArray, 0)
    next
 
    Return SetError(@error, 0, $retVal)
EndFunc   ;==>_WriteAssocToIni
 
;read AssocArray from IniFile Section
;returns number of items read - sets @error on failure
Func _ReadAssocFromIni($myIni = 'config', $mySection = '', $sSep = "|")
    $myIni = $myIni&".ini"
    Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1)
 
    If $mySection == '' Then
        $aSection = IniReadSectionNames ($myIni); All sections
        If @error Then Return SetError(@error, 0, 0)
    Else
        Dim $aSection[2] = [1,$mySection]; specific Section
    EndIf
 
    For $i = 1 To UBound($aSection)-1
 
        Local $sectionArray = IniReadSection($myIni, $aSection[$i])
        If @error Then Return SetError(1, 0, 0)
        For $x = 1 To $sectionArray[0][0]
            If StringInStr($sectionArray[$x][1], $sSep) then
                $posS = _MakePosArray($sectionArray[$x][1], $sSep)
            Else
                $posS = $sectionArray[$x][1]
            EndIf
            x($sIni&"."&$aSection[$i]&"."&$sectionArray[$x][0], $posS)
        Next
 
    next
    Return $sectionArray[0][0]
EndFunc   ;==>_ReadAssocFromIni
 
;makes a Position string using '#' number separator
Func _MakePosString($posArray, $sSep = "|")
    Local $str = ""
    For $x = 0 To UBound($posArray) - 2
        $str &= String($posArray[$x]) & $sSep
    Next
    $str &= String($posArray[UBound($posArray) - 1])
    Return $str
EndFunc   ;==>_MakePosString
 
;makes a Position array from a Position string
Func _MakePosArray($posString, $sSep = "|")
    Return StringSplit($posString, $sSep, 2)
EndFunc   ;==>_MakePosArray

GetOpt.au3

GetOpt.au3
; #INDEX# ======================================================================
; Title .........: GetOpt.au3
; Version  ......: 1.3
; Language ......: English
; Author(s) .....: dany
; Link ..........: http://www.autoitscript.com/forum/topic/143167-getoptau3-udf-to-parse-the-command-line/
; Description ...: AutoIt3 port of getopt(). Parse a command line c-style like
;                  using _GetOpt in a loop. GNU style options with - (and -- for
;                  long options) are supported as well as Windows / style options.
;                  See Forum link for an example.
;                  See http://www.gnu.org/software/libc/manual/html_node/Getopt.html
;                  for more information.
; Remarks .......: TODO:
;                  + Optionally enforce use of one option style only, no mixing.
;                  * Rewrite code to use a ByRef array as return value(s) where
;                    possible and Local-ize the rest. This would be version 2.0
;                    to mark fundamental difference in implementation.
;                  * More documentation in the source.
;                  CHANGELOG:
;                  Version 1.3:
;                  + Added support for -- (marks end of options).
;                  + Added support for + option modifiers e.g. +x.
;                  + Added support for /- option modifiers e.g. /-X.
;                  + Added _GetOpt_Sub to iterate through comma-separated
;                    suboptions like -s=a=foo,b=bar.
;                  * Changed $GETOPT_REQUIRED_ARGUMENT from keyword Default to
;                    Chr(127), keyword can now be used as an option argument.
;                  * Standardized comments and function headers.
;                  * Tidy-ed up source code.
;                  Version 1.2:
;                  + Support for required arguments with options, e.g.
;                    _GetOpt('ab:c') where -b=foo is valid and -b will return
;                    an error.
;                  + Added support for /C:foo (colon) when using DOS style.
;                  + Added optional auto-casting of command line arguments from
;                    Strings to AutoIt variants, e.g. -a=yes on the CLI would set
;                    the $GetOpt_Arg to True and not 'yes'. See __GetOpt_Cast
;                  * Private __GetOpt_DOSToGNU to simplify code.
;                  Version 1.1:
;                  * Initial public release.
; ==============================================================================
 
; #CURRENT# ====================================================================
;_GetOpt_Set
;_GetOpt
;_GetOpt_Raw
;_GetOpt_Oper
;_GetOpt_Sub
;_GetOpt_Rewind
; ==============================================================================
 
; #INTERNAL_USE_ONLY# ==========================================================
;__GetOpt_StartUp
;__GetOpt_DOSToGNU
;__GetOpt_ParseOpt
;__GetOpt_SubStart
;__GetOpt_SubStop
;__GetOpt_Cast
; ==============================================================================
 
#include-once
 
; #CONSTANTS# ==================================================================
Global Const $GETOPT_VERSION = '1.3' ;  String: Version number.
Global Const $GETOPT_REQUIRED_ARGUMENT = Chr(127) ; String: Value to use for required arguments.
Global Enum $GETOPT_MOD_NONE = 0, $GETOPT_MOD_PLUS, $GETOPT_MOD_MINUS ; Int: Option modifiers.
 
; Int: @extended error return codes.
Global Enum $E_GETOPT_BAD_FUNCTION_ARGUMENT = 1, _
		$E_GETOPT_INVALID_OPTIONS, $E_GETOPT_NO_OPTIONS_SET, _
		$E_GETOPT_NO_COMMAND_LINE, $E_GETOPT_NO_OPTIONS, $E_GETOPT_NO_OPERANDS, _
		$E_GETOPT_UNKNOWN_OPTION, $E_GETOPT_MISSING_ARGUMENT, _
		$E_GETOPT_SUBOPTION_MISMATCH, $E_GETOPT_NO_SUBOPTIONS, $E_GETOPT_UNKNOWN_SUBOPTION
 
; #VARIABLES# ==================================================================
;Global $GetOpt_OptionStyle = 0 ; 0 = Mix (default), 1 = /Windows, 2 = --gnu ; Force style, not yet implemented.
Global $GetOpt_CastArguments = True ; Boolean: Whether or not to attempt to cast arguments to AutoIt variants when quering them. Default True.
Global $GetOpt_SubOptSeparator = ',' ; String: Suboption separator. Default ',' (comma).
 
Global $GetOpt_Opt = '' ; String: Current option.
Global $GetOpt_Long = '' ; String: Current long option, if any.
Global $GetOpt_Arg = '' ; Mixed: Argument for current option, if any.
Global $GetOpt_Ind = 0 ; Int: Option index in $GetOpt_Opts.
Global $GetOpt_Mod = 0 ; Int: Indicates option modifier. See $GETOPT_MOD_* constants.
Global $GetOpt_IndRaw = 0 ; Int: Raw option index in $GetOpt_Opts.
Global $GetOpt_OperInd = 0 ; Int: Operand index in $GetOpt_Opers.
Global $GetOpt_Opts[1] = [0] ; Array: Options.
Global $GetOpt_Opers[1] = [0] ; Array: Operands (non-options).
Global $GetOpt_SubOpt = '' ; String: Current suboption, if any.
Global $GetOpt_SubArg = '' ; Mixed: Current argument for suboption, if any.
Global $GetOpt_SubInd = 0 ; Int: Suboption index in $GetOpt_SubOpts.
Global $GetOpt_SubOpts[1] = [0] ; Array: Suboptions.
 
Global $GetOpt_ArgC = 0 ; Int: Number of arguments passed, this includes the running script/program ($CmdLine[0] + 1).
Global $GetOpt_ArgV[1] = [@ScriptName] ; Array: Array of arguments passed, this includes the running script/program at index 0 (modified $CmdLine).
 
; #INTERNAL_USE_ONLY# ==========================================================
Local $__GetOpt_fStartUp = False
Local $__GetOpt_aOpts[1][3] = [[0, 0, 0]]
Local $__GetOpt_fSubStart = False
Local $__GetOpt_sSubOpt = ''
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt_Set
; Description ...: Set an array of options used by the program.
; Syntax.........: _GetOpt_Set($aOpts)
; Parameters ....: $aOpts  - Array: 2-dim array with program options.
; Return values .: Success - Int: Returns 1.
;                  Failure - Int: Returns 0, sets @error to 1 and sets @extended:
;                  |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant.
;                  |2 $E_GETOPT_INVALID_OPTIONS The options array has a wrong
;                  +number of subscripts.
;                  |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse.
; Author ........: dany
; Modified ......:
; Remarks .......: The options array has three entries:
;                  |[0][0] Short option.
;                  |[0][1] Long option version.
;                  |[0][2] Default value.
;                  |Set the value to $GETOPT_REQUIRED_ARGUMENT if the option
;                  +requires an argument to be passed.
; Related .......: _GetOpt, _GetOpt_Raw, _GetOpt_Oper, _GetOpt_Rewind
;===============================================================================
Func _GetOpt_Set($aOpts)
	If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0)
	If Not IsArray($aOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0)
	If 3 > UBound($aOpts, 2) Then Return SetError(1, $E_GETOPT_INVALID_OPTIONS, 0)
	Local $i, $iMax = UBound($aOpts)
	ReDim $__GetOpt_aOpts[$iMax + 1][3]
	$__GetOpt_aOpts[0][0] = $iMax
	For $i = 0 To $iMax - 1
		$aOpts[$i][0] = __GetOpt_DOSToGNU($aOpts[$i][0])
		$aOpts[$i][1] = __GetOpt_DOSToGNU($aOpts[$i][1])
		$__GetOpt_aOpts[$i + 1][0] = $aOpts[$i][0]
		$__GetOpt_aOpts[$i + 1][1] = $aOpts[$i][1]
		$__GetOpt_aOpts[$i + 1][2] = $aOpts[$i][2]
	Next
	__GetOpt_StartUp()
	Return 1
EndFunc   ;==>_GetOpt_Set
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt
; Description ...: Get the next available option from the command line.
; Syntax.........: _GetOpt($sOpts)
; Parameters ....: $sOpts  - String: Containing all available options.
; Return values .: Success - String: The current option.
;                  Failure - String: '?' in case the option is not recognized.
;                  |- String: ':' in case the option is missing a required argument.
;                  |- Int: 0 if anything else went wrong or no options are left.
;                  |Sets @error to 1 and sets @extended:
;                  |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant.
;                  |2 $E_GETOPT_NO_OPTIONS_SET No program options set.
;                  |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse.
;                  |5 $E_GETOPT_NO_OPTIONS No options passed.
;                  |7 $E_GETOPT_UNKNOWN_OPTION Current option is unknown.
;                  |8 $E_GETOPT_MISSING_ARGUMENT The current option is missing a
;                  +required argument.
; Author ........: dany
; Modified ......:
; Remarks .......: $sOpts Something like 'abc' where each letter represents an
;                  +option as set with _GetOpt_Set. Options with a colon : require
;                  +an argument ('ab:c').
; Related .......: _GetOpt_Raw, _GetOpt_Oper
;===============================================================================
Func _GetOpt($sOpts)
	If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0)
	If Not IsString($sOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0)
	__GetOpt_StartUp()
	If 0 = $GetOpt_Opts[0] Then Return SetError(1, $E_GETOPT_NO_OPTIONS, 0)
	If $GetOpt_Ind + 1 > $GetOpt_Opts[0] Then Return 0
	Local $i, $aOpt, $iLong = 0, $sLongOpt = ''
	$GetOpt_Opt = ''
	$GetOpt_Long = ''
	$GetOpt_Arg = ''
	$GetOpt_Mod = $GETOPT_MOD_NONE
	$aOpt = __GetOpt_ParseOpt($GetOpt_Opts[$GetOpt_Ind + 1])
	If '--' = $aOpt[0] Then Return 0
	If IsArray($__GetOpt_aOpts) Then
		If '+' = StringLeft($aOpt[0], 1) Then
			$aOpt[0] = StringReplace($aOpt[0], '+', '-', 1)
			$GetOpt_Mod = $GETOPT_MOD_PLUS
		ElseIf '*' = StringLeft($aOpt[0], 1) Then
			$aOpt[0] = StringReplace($aOpt[0], '*', '-', 1)
			$GetOpt_Mod = $GETOPT_MOD_MINUS
		EndIf
		If '--' = StringLeft($aOpt[0], 2) Then $iLong = 1
		For $i = 1 To $__GetOpt_aOpts[0][0]
			If $aOpt[0] = $__GetOpt_aOpts[$i][$iLong] Then
				$aOpt[0] = $__GetOpt_aOpts[$i][0]
				$sLongOpt = $__GetOpt_aOpts[$i][1]
				If '' = $aOpt[1] Then
					$aOpt[1] = $__GetOpt_aOpts[$i][2]
				Else
					If $GetOpt_CastArguments Then $aOpt[1] = __GetOpt_Cast($aOpt[1])
				EndIf
				ExitLoop
			EndIf
		Next
	Else
		Return SetError(1, $E_GETOPT_NO_OPTIONS_SET, 0)
	EndIf
	$aOpt[0] = StringReplace($aOpt[0], '-', '')
	$GetOpt_Opt = $aOpt[0]
	$GetOpt_Long = $sLongOpt
	$GetOpt_Arg = $aOpt[1]
	$GetOpt_Ind += 1
	; recycle
	$iLong = StringInStr($sOpts, $aOpt[0])
	If Not $iLong Then Return SetError(1, $E_GETOPT_UNKNOWN_OPTION, '?')
	If ':' = StringMid($sOpts, $iLong + 1, 1) Then
		If $GETOPT_REQUIRED_ARGUMENT = $aOpt[1] Then Return SetError(1, $E_GETOPT_MISSING_ARGUMENT, ':')
	EndIf
	Return $GetOpt_Opt
EndFunc   ;==>_GetOpt
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt_Raw
; Description ...: Get the next available option from the command line.
; Syntax.........: _GetOpt_Raw()
; Return values .: Success - String: The current raw option as in $CmdLine.
;                  Failure - Int: Returns 0, sets @error to 1 and sets @extended:
;                  |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse.
;                  |5 $E_GETOPT_NO_OPTIONS No options passed.
; Author ........: dany
; Modified ......:
; Remarks .......:
; Related .......: _GetOpt, _GetOpt_Oper
;===============================================================================
Func _GetOpt_Raw()
	If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0)
	__GetOpt_StartUp()
	If 0 = $GetOpt_Opts[0] Then Return SetError(1, $E_GETOPT_NO_OPTIONS, 0)
	If $GetOpt_IndRaw + 1 > $GetOpt_Opts[0] Then Return 0
	$GetOpt_IndRaw += 1
	Return $GetOpt_Opts[$GetOpt_IndRaw]
EndFunc   ;==>_GetOpt_Raw
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt_Oper
; Description ...: Get the next available operand from the command line.
; Syntax.........: _GetOpt_Oper()
; Return values .: Success - String: The current operand.
;                  Failure - Int: Returns 0, sets @error to 1 and sets @extended:
;                  |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse.
;                  |6 $E_GETOPT_NO_OPERANDS No operands passed.
; Author ........: dany
; Modified ......:
; Remarks .......:
; Related .......: _GetOpt, _GetOpt_Raw
;===============================================================================
Func _GetOpt_Oper()
	If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0)
	__GetOpt_StartUp()
	If 0 = $GetOpt_Opers[0] Then Return SetError(1, $E_GETOPT_NO_OPERANDS, 0)
	If $GetOpt_OperInd + 1 > $GetOpt_Opers[0] Then Return 0
	$GetOpt_OperInd += 1
	Return $GetOpt_Opers[$GetOpt_OperInd]
EndFunc   ;==>_GetOpt_Oper
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt_Sub
; Description ...: Get the next available suboption from an option.
; Syntax.........: _GetOpt_Sub($sSubOption, $aSubOpts)
; Parameters ....: $sSubOption - String:
;                  $aSubOpts - Array:
; Return values .: Success - String: The current suboption.
;                  Failure - Int: Returns 0, sets @error to 1 and sets @extended:
;                  |1 $E_GETOPT_BAD_FUNCTION_ARGUMENT Argument is wrong variant.
;                  |4 $E_GETOPT_NO_COMMAND_LINE No command line to parse.
;                  |9 $E_GETOPT_SUBOPTION_MISMATCH Passed suboption is different
;                  +from the one stored internally.
;                  |10 $E_GETOPT_NO_SUBOPTIONS No suboptions to parse.
;                  |11 $E_GETOPT_UNKNOWN_SUBOPTION Current suboption is unknown.
; Author ........: dany
; Modified ......:
; Remarks .......:
; Related .......: _GetOpt
;===============================================================================
Func _GetOpt_Sub($sSubOption, $aSubOpts)
	If 0 = $CmdLine[0] Then Return SetError(1, $E_GETOPT_NO_COMMAND_LINE, 0)
	If Not IsString($sSubOption) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0)
	If Not IsArray($aSubOpts) Then Return SetError(1, $E_GETOPT_BAD_FUNCTION_ARGUMENT, 0)
	If Not $__GetOpt_fSubStart Then __GetOpt_SubStart($sSubOption)
	If $__GetOpt_sSubOpt <> $sSubOption Then Return SetError(1, $E_GETOPT_SUBOPTION_MISMATCH, 0)
	If 0 = $GetOpt_SubOpts[0] Then
		__GetOpt_SubStop()
		Return SetError(1, $E_GETOPT_NO_SUBOPTIONS, 0)
	EndIf
	If $GetOpt_SubInd + 1 > $GetOpt_SubOpts[0] Then
		__GetOpt_SubStop()
		Return 0
	EndIf
	Local $i, $iMax = UBound($aSubOpts) - 1
	Local $aParsed = __GetOpt_ParseOpt($GetOpt_SubOpts[$GetOpt_SubInd + 1])
	$GetOpt_SubInd += 1
	$GetOpt_SubOpt = $aParsed[0]
	$GetOpt_SubArg = $aParsed[1]
	For $i = 0 To $iMax
		If $aSubOpts[$i][0] = $aParsed[0] Then
			If '' = $GetOpt_SubArg Then
				$GetOpt_SubArg = $aSubOpts[$i][1]
			Else
				If $GetOpt_CastArguments Then $GetOpt_SubArg = __GetOpt_Cast($GetOpt_SubArg)
			EndIf
			Return $GetOpt_SubOpt
		EndIf
	Next
	Return SetError(1, $E_GETOPT_UNKNOWN_SUBOPTION, '?')
EndFunc   ;==>_GetOpt_Sub
 
; #FUNCTION# ===================================================================
; Name...........: _GetOpt_Rewind
; Description ...: Rewind all iteration functions and variables.
; Syntax.........: _GetOpt_Rewind()
; Return values .: 1, always.
; Author ........: dany
; Modified ......:
; Remarks .......:
; Related .......: _GetOpt, _GetOpt_Raw, _GetOpt_Oper
;===============================================================================
Func _GetOpt_Rewind()
	__GetOpt_StartUp()
	$GetOpt_Opt = ''
	$GetOpt_Long = ''
	$GetOpt_Arg = ''
	$GetOpt_Ind = 0
	$GetOpt_Mod = 0
	$GetOpt_IndRaw = 0
	$GetOpt_OperInd = 0
	__GetOpt_SubStop()
	Return 1
EndFunc   ;==>_GetOpt_Rewind
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_StartUp
; Description ...: Parses raw command line and normalizes arguments to GNU style.
; Syntax.........: __GetOpt_StartUp()
; Return values .: 1, always.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_StartUp()
	If $__GetOpt_fStartUp Then Return 1
	Local $i, $fEndOfOpts = False, $aParsed = $CmdLine
	$GetOpt_ArgC = $CmdLine[0] + 1
	ReDim $GetOpt_ArgV[$GetOpt_ArgC]
	For $i = 1 To $aParsed[0]
		$GetOpt_ArgV[$i] = $aParsed[$i]
		$aParsed[$i] = __GetOpt_DOSToGNU($aParsed[$i])
		If Not $fEndOfOpts And StringRegExp($aParsed[$i], '^[-+*]') Then
			$GetOpt_Opts[0] += 1
			ReDim $GetOpt_Opts[$GetOpt_Opts[0] + 1]
			$GetOpt_Opts[$GetOpt_Opts[0]] = $aParsed[$i]
			If '--' = $aParsed[$i] Then $fEndOfOpts = True
		EndIf
	Next
	For $i = 1 To $aParsed[0]
		If Not StringInStr('-+*', StringLeft($aParsed[$i], 1)) Then
			$GetOpt_Opers[0] += 1
			ReDim $GetOpt_Opers[$GetOpt_Opers[0] + 1]
			$GetOpt_Opers[$GetOpt_Opers[0]] = $aParsed[$i]
		EndIf
	Next
	$__GetOpt_fStartUp = True
	Return _GetOpt_Rewind()
EndFunc   ;==>__GetOpt_StartUp
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_DOSToGNU
; Description ...: Translates DOS style options to GNU style.
; Syntax.........: __GetOpt_DOSToGNU($sOpt)
; Parameters ....: $sOpt   - String: Current raw option.
; Return values .: String: Translated GNU style option.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_DOSToGNU($sOpt)
	If StringInStr($sOpt, ':') And Not StringInStr($sOpt, '=') Then
		If StringRegExp($sOpt, '^[/+-]') Then $sOpt = StringReplace($sOpt, ':', '=', 1)
	EndIf
	If '/' = StringLeft($sOpt, 1) And '//' <> StringLeft($sOpt, 2) Then
		$sOpt = StringReplace($sOpt, '/-', '*', 1)
		If 0 = @extended Then $sOpt = StringReplace($sOpt, '/', '-', 1)
		If 2 < StringLen($sOpt) And '=' <> StringMid($sOpt, 3, 1) Then
			$sOpt = StringReplace($sOpt, '-', '--', 1)
		EndIf
	EndIf
	Return $sOpt
EndFunc   ;==>__GetOpt_DOSToGNU
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_ParseOpt
; Description ...: Splits raw option into option and passed argument.
; Syntax.........: __GetOpt_ParseOpt($sOpt)
; Parameters ....: $sOpt   - String: Current raw option.
; Return values .: Array: With index 0 = option and 1 = argument.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_ParseOpt($sOpt)
	Local $iPos, $aParsed[2]
	$iPos = StringInStr($sOpt, '=')
	If 0 = $iPos Then $iPos = StringLen($sOpt) + 1
	$aParsed[0] = StringMid($sOpt, 1, $iPos - 1)
	$aParsed[1] = StringMid($sOpt, $iPos + 1)
	Return $aParsed
EndFunc   ;==>__GetOpt_ParseOpt
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_SubStart
; Description ...: Sets variables for use with _GetOpt_Sub.
; Syntax.........: __GetOpt_SubStart($sSubOption)
; Return values .: 1, always.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_SubStart($sSubOption)
	If $__GetOpt_fSubStart Then Return 1
	__GetOpt_SubStop()
	Local $i, $aSubOpts = StringSplit($sSubOption, $GetOpt_SubOptSeparator)
	$__GetOpt_sSubOpt = $sSubOption
	ReDim $GetOpt_SubOpts[1]
	$GetOpt_SubOpts[0] = 0
	For $i = 1 To $aSubOpts[0]
		$GetOpt_SubOpts[0] += 1
		ReDim $GetOpt_SubOpts[$GetOpt_SubOpts[0] + 1]
		$GetOpt_SubOpts[$GetOpt_SubOpts[0]] = $aSubOpts[$i]
	Next
	$__GetOpt_fSubStart = True
	Return 1
EndFunc   ;==>__GetOpt_SubStart
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_SubStop
; Description ...: Resets all variables dealing with suboptions.
; Syntax.........: __GetOpt_SubStop()
; Return values .: 1, always.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_SubStop()
	ReDim $GetOpt_SubOpts[1]
	$GetOpt_SubOpts[0] = 0
	$GetOpt_SubOpt = ''
	$GetOpt_SubArg = ''
	$GetOpt_SubInd = 0
	$__GetOpt_sSubOpt = ''
	$__GetOpt_fSubStart = False
	Return 1
EndFunc   ;==>__GetOpt_SubStop
 
; #INTERNAL_USE_ONLY# ==========================================================
; Name...........: __GetOpt_Cast
; Description ...: Attempt to cast a String to a different AutoIt variant based
;                  on it's content.
; Syntax.........: __GetOpt_Cast($sArg)
; Parameters ....: $sArg   - String: Current argument.
; Return values .: Mixed: Float, Int, Default, True, False or String variant.
; Author ........: dany
; Modified ......:
; Remarks .......: For Internal Use Only
;===============================================================================
Func __GetOpt_Cast($sArg)
	If StringIsFloat($sArg) Then Return Number($sArg)
	If StringIsInt($sArg) Then Return Int($sArg)
	If 'Default' = $sArg Then Return Default
	If StringRegExp($sArg, '^(?i)(true|y(es)?)$') Then Return True
	If StringRegExp($sArg, '^(?i)(false|no?)$') Then Return False
	Return String($sArg)
EndFunc   ;==>__GetOpt_Cast

BinaryCall.au3

BinaryCall.au3
; =============================================================================
;  AutoIt BinaryCall UDF (2014.7.24)
;  Purpose: Allocate, Decompress, And Prepare Binary Machine Code
;  Author: Ward
; =============================================================================
 
#Include-once
 
Global $__BinaryCall_Kernel32dll = DllOpen('kernel32.dll')
Global $__BinaryCall_Msvcrtdll = DllOpen('msvcrt.dll')
Global $__BinaryCall_LastError = ""
 
Func _BinaryCall_GetProcAddress($Module, $Proc)
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, 'ptr', 'GetProcAddress', 'ptr', $Module, 'str', $Proc)
	If @Error Or Not $Ret[0] Then Return SetError(1, @Error, 0)
	Return $Ret[0]
EndFunc
 
Func _BinaryCall_LoadLibrary($Filename)
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, "handle", "LoadLibraryW", "wstr", $Filename)
	If @Error Then Return SetError(1, @Error, 0)
	Return $Ret[0]
EndFunc
 
Func _BinaryCall_lstrlenA($Ptr)
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, "int", "lstrlenA", "ptr", $Ptr)
	If @Error Then Return SetError(1, @Error, 0)
	Return $Ret[0]
EndFunc
 
Func _BinaryCall_Alloc($Code, $Padding = 0)
	Local $Length = BinaryLen($Code) + $Padding
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", $Length, "dword", 0x1000, "dword", 0x40)
	If @Error Or Not $Ret[0] Then Return SetError(1, @Error, 0)
	If BinaryLen($Code) Then
		Local $Buffer = DllStructCreate("byte[" & $Length & "]", $Ret[0])
		DllStructSetData($Buffer, 1, $Code)
	EndIf
	Return $Ret[0]
EndFunc
 
Func _BinaryCall_RegionSize($Ptr)
	Local $Buffer = DllStructCreate("ptr;ptr;dword;uint_ptr;dword;dword;dword")
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, "int", "VirtualQuery", "ptr", $Ptr, "ptr", DllStructGetPtr($Buffer), "uint_ptr", DllStructGetSize($Buffer))
	If @Error Or $Ret[0] = 0 Then Return SetError(1, @Error, 0)
	Return DllStructGetData($Buffer, 4)
EndFunc
 
Func _BinaryCall_Free($Ptr)
	Local $Ret = DllCall($__BinaryCall_Kernel32dll, "bool", "VirtualFree", "ptr", $Ptr, "ulong_ptr", 0, "dword", 0x8000)
	If @Error Or $Ret[0] = 0 Then
		$Ret = DllCall($__BinaryCall_Kernel32dll, "bool", "GlobalFree", "ptr", $Ptr)
		If @Error Or $Ret[0] <> 0 Then Return SetError(1, @Error, False)
	EndIf
	Return True
EndFunc
 
Func _BinaryCall_Release($CodeBase)
	Local $Ret = _BinaryCall_Free($CodeBase)
	Return SetError(@Error, @Extended, $Ret)
EndFunc
 
Func _BinaryCall_MemorySearch($Ptr, $Length, $Binary)
	Static $CodeBase
	If Not $CodeBase Then
		If @AutoItX64 Then
			$CodeBase = _BinaryCall_Create('0x4883EC084D85C94889C8742C4C39CA72254C29CA488D141131C9EB0848FFC14C39C97414448A1408453A140874EE48FFC04839D076E231C05AC3', '', 0, True, False)
		Else
			$CodeBase = _BinaryCall_Create('0x5589E58B4D14578B4508568B550C538B7D1085C9742139CA721B29CA8D341031D2EB054239CA740F8A1C17381C1074F34039F076EA31C05B5E5F5DC3', '', 0, True, False)
		EndIf
		If Not $CodeBase Then Return SetError(1, 0, 0)
	EndIf
 
	$Binary = Binary($Binary)
	Local $Buffer = DllStructCreate("byte[" & BinaryLen($Binary) & "]")
	DllStructSetData($Buffer, 1, $Binary)
 
	Local $Ret = DllCallAddress("ptr:cdecl", $CodeBase, "ptr", $Ptr, "uint", $Length, "ptr", DllStructGetPtr($Buffer), "uint", DllStructGetSize($Buffer))
	Return $Ret[0]
EndFunc
 
Func _BinaryCall_Base64Decode($Src)
	Static $CodeBase
	If Not $CodeBase Then
		If @AutoItX64 Then
			$CodeBase = _BinaryCall_Create('0x41544989CAB9FF000000555756E8BE000000534881EC000100004889E7F3A44C89D6E98A0000004439C87E0731C0E98D0000000FB66E01440FB626FFC00FB65E020FB62C2C460FB62424408A3C1C0FB65E034189EB41C1E4024183E3308A1C1C41C1FB044509E34080FF634189CC45881C08744C440FB6DFC1E5044489DF4088E883E73CC1FF0209C7418D44240241887C08014883C10380FB63742488D841C1E3064883C60483E03F4409D841884408FF89F389C84429D339D30F8C67FFFFFF4881C4000100005B5E5F5D415CC35EC3E8F9FFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000003E0000003F3435363738393A3B3C3D00000063000000000102030405060708090A0B0C0D0E0F101112131415161718190000000000001A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233', '', 132, True, False)
		Else
			$CodeBase = _BinaryCall_Create('0x55B9FF00000089E531C05756E8F10000005381EC0C0100008B55088DBDF5FEFFFFF3A4E9C00000003B45140F8FC20000000FB65C0A028A9C1DF5FEFFFF889DF3FEFFFF0FB65C0A038A9C1DF5FEFFFF889DF2FEFFFF0FB65C0A018985E8FEFFFF0FB69C1DF5FEFFFF899DECFEFFFF0FB63C0A89DE83E630C1FE040FB6BC3DF5FEFFFFC1E70209FE8B7D1089F3881C074080BDF3FEFFFF63745C0FB6B5F3FEFFFF8BBDECFEFFFF8B9DE8FEFFFF89F083E03CC1E704C1F80209F88B7D1088441F0189D883C00280BDF2FEFFFF6374278A85F2FEFFFFC1E60683C10483E03F09F088441F0289D883C0033B4D0C0F8C37FFFFFFEB0231C081C40C0100005B5E5F5DC35EC3E8F9FFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000003E0000003F3435363738393A3B3C3D00000063000000000102030405060708090A0B0C0D0E0F101112131415161718190000000000001A1B1C1D1E1F202122232425262728292A2B2C2D2E2F30313233', '', 132, True, False)
		EndIf
		If Not $CodeBase Then Return SetError(1, 0, Binary(""))
	EndIf
 
	$Src = String($Src)
	Local $SrcLen = StringLen($Src)
	Local $SrcBuf = DllStructCreate("char[" & $SrcLen & "]")
	DllStructSetData($SrcBuf, 1, $Src)
 
	Local $DstLen = Int(($SrcLen + 2) / 4) * 3 + 1
	Local $DstBuf = DllStructCreate("byte[" & $DstLen & "]")
 
	Local $Ret = DllCallAddress("uint:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint", $SrcLen, "ptr", DllStructGetPtr($DstBuf), "uint", $DstLen)
	If $Ret[0] = 0 Then Return SetError(2, 0, Binary(""))
	Return BinaryMid(DllStructGetData($DstBuf, 1), 1, $Ret[0])
EndFunc
 
Func _BinaryCall_Base64Encode($Src)
	Static $CodeBase
	If Not $CodeBase Then
		If @AutoItX64 Then
			$CodeBase = _BinaryCall_Create('AwAAAARiAQAAAAAAAAArkuFQDAlvIp0qAgbDnjr76UDZs1EPNIP2K18t9s6SNTbd43IB7HfdyPM8VfD/o36z4AmSW2m2AIsC6Af3fKNsHU4BdQKGd0PQXHxPSX0iNqp1YAKovksqQna06NeKMoOYqryTUX4WgpHjokhp6zY2sEFSIjcL7dW3FDoNVz4bGPyZHRvjFwmqvr7YGlNYKwNoh+SYCXmIgVPVZ63Vz1fbT33/QFpWmWOeBRqs4J+c8Qp6zJFsK345Pjw0I8kMSsnho4F4oNzQ2OsAbmIioaQ6Ma2ziw5NH+M+t4SpEeHDnBdUTTL20sxWZ0yKruFAsBIRoHvM7LYcid2eBV2d5roSjnkwMG0g69LNjs1fHjbI/9iU/hJwpSsgl4fltXdZG659/li13UFY89M7UfckiZ9XOeBM0zadgNsy8r8M3rEAAA==')
		Else
			$CodeBase = _BinaryCall_Create('AwAAAARVAQAAAAAAAAAqr7blBndrIGnmhhfXD7R1fkOTKhicg1W6MCtStbz+CsneBEg0bbHH1sqTLmLfY7A6LqZl6LYWT5ULVj6MXgugPbBn9wKsSU2ZCcBBPNkx09HVPdUaKnbqghDGj/C5SHoF+A/5g+UgE1C5zJZORjJ8ljs5lt2Y9lA4BsY7jVKX2vmDvHK1NnSR6nVwh7Pb+Po/UpNcy5sObVWDKkYSCCtCIjKIYqOe3c6k8Xsp4eritCUprXEVvCFi7K5Z6HFXdm3nZsFcE+eSJ1WkRnVQbWcmpjGMGka61C68+CI7tsQ13UnCFWNSpDrCbzUejMZh8HdPgEc5vCg3pKMKin/NavNpB6+87Y9y7HIxmKsPdjDT30u9hUKWnYiRe3nrwKyVDsiYpKU/Nse368jHag5B5or3UKA+nb2+eY8JwzgA')
		EndIf
		If Not $CodeBase Then Return SetError(1, 0, Binary(""))
	EndIf
 
	$Src = Binary($Src)
	Local $SrcLen = BinaryLen($Src)
	Local $SrcBuf = DllStructCreate("byte[" & $SrcLen & "]")
	DllStructSetData($SrcBuf, 1, $Src)
 
	Local $DstLen = Int(($SrcLen + 2) / 3) * 4 + 1
	Local $DstBuf = DllStructCreate("char[" & $DstLen & "]")
 
	Local $Ret = DllCallAddress("uint:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint", $SrcLen, "ptr", DllStructGetPtr($DstBuf), "uint", $DstLen)
	If $Ret[0] = 0 Then Return Binary("")
	Return StringMid(DllStructGetData($DstBuf, 1), 1, $Ret[0])
EndFunc
 
Func _BinaryCall_LzmaDecompress($Src)
	Static $CodeBase
	If Not $CodeBase Then
		If @AutoItX64 Then
			$CodeBase = _BinaryCall_Create(_BinaryCall_Base64Decode('QVcxwEFWQVVBVFVXSInXVkiJzlMx20iB7OgAAABEiiFBgPzgdgnpyQAAAEGD7C1BiMf/wEGA/Cx38THA6wRBg+wJQYjG/8BBgPwId/GLRglEi24FQQ+2zkyJRCQoRQ+2/0HB5xBBiQFBD7bEAcG4AAMAANPgjYQAcA4AAEhjyOjIBAAATInpSInF6L0EAABIicMxwEyJ8kSI4EyLRCQoiNQl//8A/0QJ+EiF24lFAHQoTYXtdCNIjVfzSI1MJDhIg8YNTIkEJE2J6UmJ2EiJ7+g2AAAAicbrBb4BAAAASInp6IQEAACF9nQKSInZMdvodgQAAEiJ2EiBxOgAAABbXl9dQVxBXUFeQV/DVVNBV0FWQVVBVEFQTQHBQVFNicVRVkgB8lJIieX8SYn0iwdMjX8Eik8Cg8r/0+L30olV6Ijhg8r/0+L30olV5ADBiUXsuAEAAACJReCJRdyJRdhIiUXQRSnJKfaDy/8A0bgAAwAA0+BIjYg2BwAAuAAEAARMif/R6fOrvwUAAADoUAMAAP/PdfdEie9EicgrfSDB4ARBifpEI1XoRAHQTY0cR+hAAwAAD4WTAAAAik3sI33k0+eA6Qj22dPuAfe4AQAAAEiNPH++AAEAAMHnCEGD+QdNjbR/bA4AAHI0TInvSCt90A+2P9HnQYnzIf5BAfNPjRxe6O8CAACJwcHuCIPhATnOvgABAAB1DjnGd9jrDE2J8+jQAgAAOfBy9EyJ76pEiclBg/kEcg65AwAAAEGD+QpyA4PBA0EpyelDAgAAT42cT4ABAADomgIAAHUsi0XcQYP5B4lF4BnAi1XY99CLTdCD4AOJVdxBicGJTdhNjbdkBgAA6akAAABPjZxPmAEAAOhfAgAAdUZEicjB4AREAdBNjZxH4AEAAOhHAgAAdWpBg/kHuQkAAAByA4PBAkGJyUyJ70grfdBIO30gD4L9AQAAigdIA33QqumzAQAAT42cT7ABAADoCgIAAIt12HQhT42cT8gBAADo+AEAAIt13HQJi03ci3XgiU3gi03YiU3ci03QiU3YiXXQQYP5B7kIAAAAcgODwQNBiclNjbdoCgAATYnz6LsBAAB1FESJ0CnJweADvggAAABJjXxGBOs2TY1eAuicAQAAdRpEidC5CAAAAMHgA74IAAAASY28RgQBAADrEUmNvgQCAAC5EAAAAL4AAQAAiU3MuAEAAABJifvoYQEAAInCKfJy8gNVzEGD+QSJVcwPg7kAAABBg8EHuQMAAAA50XICidHB4Qa4AQAAAEmNvE9gAwAAvkAAAABJifvoHwEAAEGJwkEp8nLwQYP6BHJ4RInWRIlV0NHug2XQAf/Og03QAkGD+g5zFYnx0mXQi0XQRCnQTY20R14FAADrLIPuBOi6AAAA0evRZdBBOdhyBv9F0EEp2P/OdedNjbdEBgAAwWXQBL4EAAAAvwEAAACJ+E2J8+ioAAAAqAF0Awl90NHn/8516+sERIlV0P9F0EyJ74tNzEiJ+IPBAkgrRSBIOUXQd1RIif5IK3XQSItVGKyqSDnXcwT/yXX1SYn9D7bwTDttGA+C9fz//+gwAAAAKcBIi1UQTCtlCESJIkiLVWBMK20gRIkqSIPEKEFcQV1BXUFfW13DXli4AQAAAOvSgfsAAAABcgHDweMITDtlAHPmQcHgCEWKBCRJg8QBwynATY0cQ4H7AAAAAXMVweMITDtlAHPBQcHgCEWKBCRJg8QBidlBD7cTwekLD6/KQTnIcxOJy7kACAAAKdHB6QVmQQELAcDDKcvB6gVBKchmQSkTAcCDwAHDSLj////////////gbXN2Y3J0LmRsbHxtYWxsb2MASLj////////////gZnJlZQA='))
		Else
			$CodeBase = _BinaryCall_Create(_BinaryCall_Base64Decode('VYnlVzH/VlOD7EyLXQiKC4D54A+HxQAAADHA6wWD6S2I0ID5LI1QAXfziEXmMcDrBYPpCYjQgPkIjVABd/OIReWLRRSITeSLUwkPtsmLcwWJEA+2ReUBwbgAAwAA0+CNhABwDgAAiQQk6EcEAACJNCSJRdToPAQAAItV1InHi0Xkhf+JArgBAAAAdDaF9nQyi0UQg8MNiRQkiXQkFIl8JBCJRCQYjUXgiUQkDItFDIlcJASD6A2JRCQI6CkAAACLVdSJRdSJFCToAQQAAItF1IXAdAqJPCQx/+jwAwAAg8RMifhbXl9dw1dWU1WJ5YtFJAFFKFD8i3UYAXUcVot1FK2SUopO/oPI/9Pg99BQiPGDyP/T4PfQUADRifeD7AwpwEBQUFBQUFcp9laDy/+4AAMAANPgjYg2BwAAuAAEAATR6fOragVZ6MoCAADi+Yt9/ItF8Ct9JCH4iUXosADoywIAAA+FhQAAAIpN9CN97NPngOkI9tnT7lgB916NPH/B5wg8B1qNjH5sDgAAUVa+AAEAAFCwAXI0i338K33cD7Y/i23M0eeJ8SH+AfGNbE0A6JgCAACJwcHuCIPhATnOvgABAAB1DjnwctfrDIttzOh5AgAAOfBy9FqD+gSJ0XIJg/oKsQNyArEGKcpS60mwwOhJAgAAdRRYX1pZWln/NCRRUrpkBgAAsQDrb7DM6CwCAAB1LLDw6BMCAAB1U1g8B7AJcgKwC1CLdfwrddw7dSQPgs8BAACsi338qumOAQAAsNjo9wEAAIt12HQbsOTo6wEAAIt11HQJi3XQi03UiU3Qi03YiU3Ui03ciU3YiXXcWF9ZumgKAACxCAH6Ulc8B4jIcgIEA1CLbczovAEAAHUUi0Xoi33MweADKclqCF6NfEcE6zWLbcyDxQLomwEAAHUYi0Xoi33MweADaghZaghejbxHBAEAAOsQvwQCAAADfcxqEFm+AAEAAIlN5CnAQIn96GYBAACJwSnxcvMBTeSDfcQED4OwAAAAg0XEB4tN5IP5BHIDagNZi33IweEGKcBAakBejbxPYAMAAIn96CoBAACJwSnxcvOJTeiJTdyD+QRyc4nOg2XcAdHug03cAk6D+Q5zGbivAgAAKciJ8dJl3ANF3NHgA0XIiUXM6y2D7gToowAAANHr0WXcOV3gcgb/RdwpXeBOdei4RAYAAANFyIlFzMFl3ARqBF4p/0eJ+IttzOi0AAAAqAF0Awl93NHnTnXs6wD/RdyLTeSDwQKLffyJ+CtFJDlF3HdIif4rddyLVSisqjnXcwNJdfeJffwPtvA7fSgPgnH9///oKAAAACnAjWwkPItVIIt1+Ct1GIkyi1Usi338K30kiTrJW15fw15YKcBA69qB+wAAAAFyAcPB4whWi3X4O3Ucc+SLReDB4AisiUXgiXX4XsOLTcQPtsDB4QQDRegByOsGD7bAA0XEi23IjWxFACnAjWxFAIH7AAAAAXMci0wkOMFkJCAIO0wkXHOcihH/RCQ4weMIiFQkIInZD7dVAMHpCw+vyjlMJCBzF4nLuQAIAAAp0cHpBWYBTQABwI1sJEDDweoFKUwkICnLZilVAAHAg8ABjWwkQMO4///////gbXN2Y3J0LmRsbHxtYWxsb2MAuP//////4GZyZWUA'))
		EndIf
		If Not $CodeBase Then Return SetError(1, 0, Binary(""))
	EndIf
 
	$Src = Binary($Src)
	Local $SrcLen = BinaryLen($Src)
	Local $SrcBuf = DllStructCreate("byte[" & $SrcLen & "]")
	DllStructSetData($SrcBuf, 1, $Src)
 
	Local $Ret = DllCallAddress("ptr:cdecl", $CodeBase, "ptr", DllStructGetPtr($SrcBuf), "uint_ptr", $SrcLen, "uint_ptr*", 0, "uint*", 0)
	If $Ret[0] Then
		Local $DstBuf = DllStructCreate("byte[" & $Ret[3] & "]", $Ret[0])
		Local $Output = DllStructGetData($DstBuf, 1)
		DllCall($__BinaryCall_Msvcrtdll, "none:cdecl", "free", "ptr", $Ret[0])
 
		Return $Output
	EndIf
	Return SetError(2, 0, Binary(""))
EndFunc
 
Func _BinaryCall_Relocation($Base, $Reloc)
	Local $Size = Int(BinaryMid($Reloc, 1, 2))
 
	For $i = 3 To BinaryLen($Reloc) Step $Size
		Local $Offset = Int(BinaryMid($Reloc, $i, $Size))
		Local $Ptr = $Base + $Offset
		DllStructSetData(DllStructCreate("ptr", $Ptr), 1, DllStructGetData(DllStructCreate("ptr", $Ptr), 1) + $Base)
	Next
EndFunc
 
Func _BinaryCall_ImportLibrary($Base, $Length)
	Local $JmpBin, $JmpOff, $JmpLen, $DllName, $ProcName
	If @AutoItX64 Then
		$JmpBin = Binary("0x48B8FFFFFFFFFFFFFFFFFFE0")
		$JmpOff = 2
	Else
		$JmpBin = Binary("0xB8FFFFFFFFFFE0")
		$JmpOff = 1
	EndIf
	$JmpLen = BinaryLen($JmpBin)
 
	Do
		Local $Ptr = _BinaryCall_MemorySearch($Base, $Length, $JmpBin)
		If $Ptr = 0 Then ExitLoop
 
		Local $StringPtr = $Ptr + $JmpLen
		Local $StringLen = _BinaryCall_lstrlenA($StringPtr)
		Local $String = DllStructGetData(DllStructCreate("char[" & $StringLen & "]", $StringPtr), 1)
		Local $Split = StringSplit($String, "|")
 
		If $Split[0] = 1 Then
			$ProcName = $Split[1]
		ElseIf $Split[0] = 2 Then
			If $Split[1] Then $DllName = $Split[1]
			$ProcName = $Split[2]
		EndIf
 
		If $DllName And $ProcName Then
			Local $Handle = _BinaryCall_LoadLibrary($DllName)
			If Not $Handle Then
				$__BinaryCall_LastError = "LoadLibrary fail on " & $DllName
				Return SetError(1, 0, False)
			EndIf
 
			Local $Proc = _BinaryCall_GetProcAddress($Handle, $ProcName)
			If Not $Proc Then
				$__BinaryCall_LastError = "GetProcAddress failed on " & $ProcName
				Return SetError(2, 0, False)
			EndIf
 
			DllStructSetData(DllStructCreate("ptr", $Ptr + $JmpOff), 1, $Proc)
		EndIf
 
		Local $Diff = Int($Ptr - $Base + $JmpLen + $StringLen + 1)
		$Base += $Diff
		$Length -= $Diff
 
	Until $Length <= $JmpLen
	Return True
EndFunc
 
Func _BinaryCall_CodePrepare($Code)
	If Not $Code Then Return ""
	If IsBinary($Code) Then Return $Code
 
	$Code = String($Code)
	If StringLeft($Code, 2) = "0x" Then Return Binary($Code)
	If StringIsXDigit($Code) Then Return Binary("0x" & $Code)
 
	Return _BinaryCall_LzmaDecompress(_BinaryCall_Base64Decode($Code))
EndFunc
 
Func _BinaryCall_SymbolFind($CodeBase, $Identify, $Length = Default)
	$Identify = Binary($Identify)
 
	If IsKeyword($Length) Then
		$Length = _BinaryCall_RegionSize($CodeBase)
	EndIf
 
	Local $Ptr = _BinaryCall_MemorySearch($CodeBase, $Length, $Identify)
	If $Ptr = 0 Then Return SetError(1, 0, 0)
 
	Return $Ptr + BinaryLen($Identify)
EndFunc
 
Func _BinaryCall_SymbolList($CodeBase, $Symbol)
	If Not IsArray($Symbol) Or $CodeBase = 0 Then Return SetError(1, 0, 0)
 
	Local $Tag = ""
	For $i = 0 To UBound($Symbol) - 1
		$Tag &=  "ptr " & $Symbol[$i] & ";"
	Next
 
	Local $SymbolList = DllStructCreate($Tag)
	If @Error Then Return SetError(1, 0, 0)
 
	For $i = 0 To UBound($Symbol) - 1
		$CodeBase = _BinaryCall_SymbolFind($CodeBase, $Symbol[$i])
		DllStructSetData($SymbolList, $Symbol[$i], $CodeBase)
	Next
	Return $SymbolList
EndFunc
 
Func _BinaryCall_Create($Code, $Reloc = '', $Padding = 0, $ReleaseOnExit = True, $LibraryImport = True)
	Local $BinaryCode = _BinaryCall_CodePrepare($Code)
	If Not $BinaryCode Then Return SetError(1, 0, 0)
 
	Local $BinaryCodeLen = BinaryLen($BinaryCode)
	Local $TotalCodeLen = $BinaryCodeLen + $Padding
 
	Local $CodeBase = _BinaryCall_Alloc($BinaryCode, $Padding)
	If Not $CodeBase Then Return SetError(2, 0, 0)
 
	If $Reloc Then
		$Reloc = _BinaryCall_CodePrepare($Reloc)
		If Not $Reloc Then Return SetError(3, 0, 0)
		_BinaryCall_Relocation($CodeBase, $Reloc)
	EndIf
 
	If $LibraryImport Then
		If Not _BinaryCall_ImportLibrary($CodeBase, $BinaryCodeLen) Then
			_BinaryCall_Free($CodeBase)
			Return SetError(4, 0, 0)
		EndIf
	EndIf
 
	If $ReleaseOnExit Then
		_BinaryCall_ReleaseOnExit($CodeBase)
	EndIf
 
	Return SetError(0, $TotalCodeLen, $CodeBase)
EndFunc
 
Func _BinaryCall_CommandLineToArgv($CommandLine, ByRef $Argc, $IsUnicode = False)
	Static $SymbolList
	If Not IsDllStruct($SymbolList) Then
		Local $Code
		If @AutoItX64 Then
			$Code = 'AwAAAASuAgAAAAAAAAAkL48ClEB9jTEOeYv4yYTosNjFNgf81Ag4vS2VP4y4wxFa+4yMI7GDB7CG+xn4JE3cdEVvk8cMp4oIuS3DgTxlcKHGVIg94tvzG/256bizZfGtAETQUCPQjW5+JSx2C/Y4C0VNJMKTlSCHiV5AzXRZ5gw3WFghbtkCCFxWOX+RDSI2oH/vROEOnqc0jfKTo17EBjqX+dW3QxrUe45xsbyYTZ9ccIGySgcOAxetbRiSxQnz8BOMbJyfrbZbuVJyGpKrXFLh/5MlBZ09Cim9qgflbGzmkrGStT9QL1f+O2krzyOzgaWWqhWL6S+y0G32RWVi0uMLR/JOGLEW/+Yg/4bzkeC0lKELT+RmWAatNa38BRfaitROMN12moRDHM6LYD1lzPLnaiefSQRVti561sxni/AFkYoCb5Lkuyw4RIn/r/flRiUg5w48YkqBBd9rXkaXrEoKwPg6rmOvOCZadu//B6HN4+Ipq5aYNuZMxSJXmxwXVRSQZVpSfLS2ATZMd9/Y7kLqrKy1H4V76SgI/d9OKApfKSbQ8ZaKIHBCsoluEip3UDOB82Z21zd933UH5l0laGWLIrTz7xVGkecjo0NQzR7LyhhoV3xszlIuw2v8q0Q/S9LxB5G6tYbOXo7lLjNIZc0derZz7DNeeeJ9dQE9hp8unubaTBpulPxTNtRjog=='
		Else
			$Code = 'AwAAAAR6AgAAAAAAAABcQfD553vjya/3DmalU0BKqABevUb/60GZ55rMwmzpQfPSRUlIl04lEiS8RDrXpS0EoBUe+uzDgZd37nVu9wsJ4fykqYvLoMz3ApxQbTBKleOIRSla6I0V8dNP3P7rHeUfjH0jCho0RvhhVpf0o4ht/iZptauxaoy1zQ19TkPZ/vf5Im8ecY6qEdHNzjo2H60jVwiOJ+1J47TmQRwxJ+yKLakq8QNxtKkRIB9B9ugfo3NAL0QslDxbyU0dSgw2aOPxV+uttLzYNnWbLBZVQbchcKgLRjC/32U3Op576sOYFolB1Nj4/33c7MRgtGLjlZfTB/4yNvd4/E+u3U6/Q4MYApCfWF4R/d9CAdiwgIjCYUkGDExKjFtHbAWXfWh9kQ7Q/GWUjsfF9BtHO6924Cy1Ou+BUKksqsxmIKP4dBjvvmz9OHc1FdtR9I63XKyYtlUnqVRtKwlNrYAZVCSFsyAefMbteq1ihU33sCsLkAnp1LRZ2wofgT1/JtT8+GO2s/n52D18wM70RH2n5uJJv8tlxQc1lwbmo4XQvcbcE91U2j9glvt2wC1pkP0hF23Nr/iiIEZHIPAOAHvhervlHE830LSHyUx8yh5Tjojr0gdLvQ=='
		EndIf
		Local $CodeBase = _BinaryCall_Create($Code)
		If @Error Then Return SetError(1, 0, 0)
 
		Local $Symbol[] = ["ToArgvW","ToArgvA"]
		$SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol)
		If @Error Then Return SetError(1, 0, 0)
	EndIf
 
	Local $Ret
	If $IsUnicode Then
		$Ret = DllCallAddress("ptr:cdecl", DllStructGetData($SymbolList, "ToArgvW"), "wstr", $CommandLine, "int*", 0)
	Else
		$Ret = DllCallAddress("ptr:cdecl", DllStructGetData($SymbolList, "ToArgvA"), "str", $CommandLine, "int*", 0)
	EndIf
 
	If Not @Error And $Ret[0] <> 0 Then
		_BinaryCall_ReleaseOnExit($Ret[0])
		$Argc = $Ret[2]
		Return $Ret[0]
	Else
		Return SetError(2, 0, 0)
	EndIf
EndFunc
 
Func _BinaryCall_StdioRedirect($Filename = "CON", $Flag = 1 + 2 + 4)
	Static $SymbolList
	If Not IsDllStruct($SymbolList) Then
		Local $Code, $Reloc
		If @AutoItX64 Then
			$Code = 'AwAAAASjAQAAAAAAAAAkL48ClEB9jTEOeYv4yYTosNjFM1rLNdMULriZUDxTj+ZdkQ01F5zKL+WDCScfQKKLn66EDmcA+gXIkPcZV4lyz8VPw8BPZlNB5KymydM15kCA+uqvmBc1V0NJfzgsF0Amhn0JhM/ZIguYCHxywMQ1SgKxUb05dxDg8WlX/2aPfSolcX47+4/72lPDNTeT7d7XRdm0ND+eCauuQcRH2YOahare9ASxuU4IMHCh2rbZYHwmTNRiQUB/8dLGtph93yhmwdHtyMPLX2x5n6sdA1mxua9htLsLTulE05LLmXbRYXylDz0A'
			$Reloc = 'AwAAAAQIAAAAAAAAAAABAB7T+CzGn9ScQAC='
		Else
			$Code = 'AwAAAASVAQAAAAAAAABcQfD553vjya/3DmalU0BKqABaUcndypZ3mTYUkHxlLV/lKZPrXYWXgNATjyiowkUQGDVYUy5THQwK4zYdU7xuGf7qfVDELc1SNbiW3NgD4D6N6ZM7auI1jPaThsPfA/ouBcx2aVQX36fjmViTZ8ZLzafjJeR7d5OG5s9sAoIzFLTZsqrFlkIJedqDAOfhA/0mMrkavTWnsio6yTbic1dER0DcEsXpLn0vBNErKHoagLzAgofHNLeFRw5yHWz5owR5CYL7rgiv2k51neHBWGx97A=='
			$Reloc = 'AwAAAAQgAAAAAAAAAAABABfyHS/VRkdjBBzbtGPD6vtmVH/IsGHYvPsTv2lGuqJxGlAA'
		EndIf
 
		Local $CodeBase = _BinaryCall_Create($Code, $Reloc)
		If @Error Then Return SetError(1, 0, 0)
 
		Local $Symbol[] = ["StdinRedirect","StdoutRedirect","StderrRedirect"]
		$SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol)
		If @Error Then Return SetError(1, 0, 0)
	EndIf
 
	If BitAND($Flag, 1) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StdinRedirect"), "str", $Filename)
	If BitAND($Flag, 2) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StdoutRedirect"), "str", $Filename)
	If BitAND($Flag, 4) Then DllCallAddress("none:cdecl", DllStructGetData($SymbolList, "StderrRedirect"), "str", $Filename)
EndFunc
 
Func _BinaryCall_StdinRedirect($Filename = "CON")
	Local $Ret = _BinaryCall_StdioRedirect($Filename, 1)
	Return SetError(@Error, @Extended, $Ret)
EndFunc
 
Func _BinaryCall_StdoutRedirect($Filename = "CON")
	Local $Ret = _BinaryCall_StdioRedirect($Filename, 2)
	Return SetError(@Error, @Extended, $Ret)
EndFunc
 
Func _BinaryCall_StderrRedirect($Filename = "CON")
	Local $Ret = _BinaryCall_StdioRedirect($Filename, 4)
	Return SetError(@Error, @Extended, $Ret)
EndFunc
 
Func _BinaryCall_ReleaseOnExit($Ptr)
	OnAutoItExitRegister('__BinaryCall_DoRelease')
	__BinaryCall_ReleaseOnExit_Handle($Ptr)
EndFunc
 
Func __BinaryCall_DoRelease()
	__BinaryCall_ReleaseOnExit_Handle()
EndFunc
 
Func __BinaryCall_ReleaseOnExit_Handle($Ptr = Default)
	Static $PtrList
 
	If @NumParams = 0 Then
		If IsArray($PtrList) Then
			For $i = 1 To $PtrList[0]
				_BinaryCall_Free($PtrList[$i])
			Next
		EndIf
	Else
		If Not IsArray($PtrList) Then
			Local $InitArray[1] = [0]
			$PtrList = $InitArray
		EndIf
 
		If IsPtr($Ptr) Then
			Local $Array = $PtrList
			Local $Size = UBound($Array)
			ReDim $Array[$Size + 1]
			$Array[$Size] = $Ptr
			$Array[0] += 1
			$PtrList = $Array
		EndIf
	EndIf
EndFunc

Json.au3

Json.au3
; ============================================================================================================================
; File		: Json.au3 (2018.12.29)
; Purpose	: A Non-Strict JavaScript Object Notation (JSON) Parser UDF
; Author	: Ward
; Dependency: BinaryCall.au3
; Website	: http://www.json.org/index.html
;
; Source	: jsmn.c
; Author	: zserge
; Website	: http://zserge.com/jsmn.html
;
; Source	: json_string_encode.c, json_string_decode.c
; Author	: Ward
;             Jos - Added Json_Dump()
;             TheXMan - Json_ObjGetItems and some Json_Dump Fixes.
;             Jos - Changed Json_ObjGet() and Json_ObjExists() to allow for multilevel object in string.
; ============================================================================================================================
 
; ============================================================================================================================
; Public Functions:
;   Json_StringEncode($String, $Option = 0)
;   Json_StringDecode($String)
;   Json_IsObject(ByRef $Object)
;   Json_IsNull(ByRef $Null)
;   Json_Encode($Data, $Option = 0, $Indent = Default, $ArraySep = Default, $ObjectSep = Default, $ColonSep = Default)
;   Json_Decode($Json, $InitTokenCount = 1000)
;   Json_ObjCreate()
;   Json_ObjPut(ByRef $Object, $Key, $Value)
;   Json_ObjGet(ByRef $Object, $Key)
;   Json_ObjDelete(ByRef $Object, $Key)
;   Json_ObjExists(ByRef $Object, $Key)
;   Json_ObjGetCount(ByRef $Object)
;   Json_ObjGetKeys(ByRef $Object)
;   Json_ObjGetItems(ByRef $Object)
;   Json_ObjClear(ByRef $Object)
;   Json_Put(ByRef $Var, $Notation, $Data, $CheckExists = False)
;   Json_Get(ByRef $Var, $Notation)
;   Json_Dump($String)
; ============================================================================================================================
 
#include-once
#include "BinaryCall.au3"
 
; The following constants can be combined to form options for Json_Encode()
Global Const $JSON_UNESCAPED_UNICODE = 1 ; Encode multibyte Unicode characters literally
Global Const $JSON_UNESCAPED_SLASHES = 2 ; Don't escape /
Global Const $JSON_HEX_TAG = 4 ; All < and > are converted to \u003C and \u003E
Global Const $JSON_HEX_AMP = 8 ; All &s are converted to \u0026
Global Const $JSON_HEX_APOS = 16 ; All ' are converted to \u0027
Global Const $JSON_HEX_QUOT = 32 ; All " are converted to \u0022
Global Const $JSON_UNESCAPED_ASCII = 64 ; Don't escape ascii charcters between chr(1) ~ chr(0x1f)
Global Const $JSON_PRETTY_PRINT = 128 ; Use whitespace in returned data to format it
Global Const $JSON_STRICT_PRINT = 256 ; Make sure returned JSON string is RFC4627 compliant
Global Const $JSON_UNQUOTED_STRING = 512 ; Output unquoted string if possible (conflicting with $Json_STRICT_PRINT)
 
; Error value returnd by Json_Decode()
Global Const $JSMN_ERROR_NOMEM = -1 ; Not enough tokens were provided
Global Const $JSMN_ERROR_INVAL = -2 ; Invalid character inside JSON string
Global Const $JSMN_ERROR_PART = -3 ; The string is not a full JSON packet, more bytes expected
Global $Total_JSON_DUMP_Output = ""
 
Func __Jsmn_RuntimeLoader($ProcName = "")
	Static $SymbolList
	If Not IsDllStruct($SymbolList) Then
		Local $Code
		If @AutoItX64 Then
			$Code = 'AwAAAAQfCAAAAAAAAAA1HbEvgTNrvX54gCiWSTVmt5v7RCdoFJ/zhkKmwcm8yVqZPjJBoVhNHHAIzrHWKbZh1J0QAUaHB5zyQTilTmWa9O0OKeLrk/Jg+o7CmMzjEk74uPongdHv37nwYXvg97fiHvjP2bBzI9gxSkKq9Cqh/GxSHIlZPYyW76pXUt//25Aqs2Icfpyay/NFd50rW7eMliH5ynkrp16HM1afithVrO+LpSaz/IojowApmXnBHUncHliDqbkx6/AODUkyDm1hj+AiEZ9Me1Jy+hBQ1/wC/YnuuYSJvNAKp6XDnyc8Nwr54Uqx5SbUW2CezwQQ7aXX/HFiHSKpQcFW/gi8oSx5nsoxUXVjxeNI/L7z6GF2mfu3Tnpt7hliWEdA2r2VB+TIM7Pgwl9X3Ge0T3KJQUaRtLJZcPvVtOuKXr2Q9wy7hl80hVRrt9zYrbjBHXLrRx/HeIMkZwxhmKo/dD/vvaNgE+BdU8eeJqFBJK2alrK2rh2WkRynftyepm1WrdKrz/5KhQPp/4PqH+9IADDjoGBbfvJQXdT+yiO8DtfrVnd+JOEKsKEsdgeM3UXx5r6tEHO9rYWbzbnyEiX7WozZemry+vBZMMtHn1aA63+RcDQED73xOsnj00/9E5Z6hszM5Hi8vi6Hw3iOgf3cHwcXG44aau0JpuA2DlrUvnJOYkNnY+bECeSdAR1UQkFNyqRoH2xm4Y7gYMCPsFtPBlwwleEKI27SsUq1ZHVQvFCoef7DXgf/GwPCAvwDMIQfb3hJtIVubOkASRQZVNIJ/y4KPrn/gcASV7fvMjE34loltTVlyqprUWxpI51tN6vhTOLAp+CHseKxWaf9g1wdbVs0e/5xAiqgJbmKNi9OYbhV/blpp3SL63XKxGiHdxhK1aR+4rUY4eckNbaHfW7ob+q7aBoHSs6LVX9lWakb/xWxwQdwcX/7/C+TcQSOOg6rLoWZ8wur9qp+QwzoCbXkf04OYpvD5kqgEiwQnB90kLtcA+2XSbDRu+aq02eNNCzgkZujeL/HjVISjf2EuQKSsZkBhS15eiXoRgPaUoQ5586VS7t7rhM8ng5LiVzoUQIZ0pNKxWWqD+gXRBvOMIXY2yd0Ei4sE5KFIEhbs3u8vwP7nFLIpZ/RembPTuc0ZlguGJgJ2F5iApfia+C2tRYRNjVCqECCveWw6P2Btfaq9gw7cWWmJflIQbjxtccDqsn52cftLqXSna9zk05mYdJSV8z2W7vM1YJ5Rd82v0j3kau710A/kQrN41bdaxmKjL+gvSRlOLB1bpvkCtf9+h+eVA4XIkIXKFydr1OjMZ8wq2FIxPJXskAe4YMgwQmeWZXMK1KBbLB3yQR1YOYaaHk1fNea9KsXgs5YLbiP/noAusz76oEDo/DJh1aw7cUwdhboVPg1bNq88mRb5RGa13KDK9uEET7OA02KbSL+Q4HOtyasLUoVrZzVyd8iZPoGrV36vHnj+yvG4fq6F/fkug/sBRp186yVZQVmdAgFd+WiRLnUjxHUKJ6xBbpt4FTP42E/PzPw3JlDb0UQtXTDnIL0CWqbns2E7rZ5PBwrwQYwvBn/gaEeLVGDSh84DfW4zknIneGnYDXdVEHC+ITzejAnNxb1duB+w2aVTk64iXsKHETq53GMH6DuFi0oUeEFb/xp0HsRyNC8vBjOq3Kk7NZHxCQLh7UATFttG7sH+VIqGjjNwmraGJ0C92XhpQwSgfAb3KHucCHGTTti0sn6cgS3vb36BkjGKsRhXVuoQCFH96bvTYtl8paQQW9ufRfvxPqmU0sALdR0fIvZwd7Z8z0UoEec6b1Sul4e60REj/H4scb6N2ryHBR9ua5N1YxJu1uwgoLXUL2wT9ZPBjPjySUzeqXikUIKKYgNlWy+VlNIiWWTPtKpCTr508logA=='
		Else
			$Code = 'AwAAAASFBwAAAAAAAAA1HbEvgTNrvX54gCiqsa1mt5v7RCdoAFjCfVE40DZbE5UfabA9UKuHrjqOMbvjSoB2zBJTEYEQejBREnPrXL3VwpVOW+L9SSfo0rTfA8U2W+Veqo1uy0dOsPhl7vAHbBHrvJNfEUe8TT0q2eaTX2LeWpyrFEm4I3mhDJY/E9cpWf0A78e+y4c7NxewvcVvAakIHE8Xb8fgtqCTVQj3Q1eso7n1fKQj5YsQ20A86Gy9fz8dky78raeZnhYayn0b1riSUKxGVnWja2i02OvAVM3tCCvXwcbSkHTRjuIAbMu2mXF1UpKci3i/GzPmbxo9n/3aX/jpR6UvxMZuaEDEij4yzfZv7EyK9WCNBXxMmtTp3Uv6MZsK+nopXO3C0xFzZA/zQObwP3zhJ4sdatzMhFi9GAM70R4kgMzsxQDNArueXj+UFzbCCFZ89zXs22F7Ixi0FyFTk3jhH56dBaN65S+gtPztNGzEUmtk4M8IanhQSw8xCXr0x0MPDpDFDZs3aN5TtTPYmyk3psk7OrmofCQGG5cRcqEt9902qtxQDOHumfuCPMvU+oMjzLzBVEDnBbj+tY3y1jvgGbmEJguAgfB04tSeAt/2618ksnJJK+dbBkDLxjB4xrFr3uIFFadJQWUckl5vfh4MVXbsFA1hG49lqWDa7uSuPCnOhv8Yql376I4U4gfcF8LcgorkxS+64urv2nMUq6AkBEMQ8bdkI64oKLFfO7fGxh5iMNZuLoutDn2ll3nq4rPi4kOyAtfhW0UPyjvqNtXJ/h0Wik5Mi8z7BVxaURTDk81TP8y9+tzjySB/uGfHFAzjF8DUY1vqJCgn0GQ8ANtiiElX/+Wnc9HWi2bEEXItbm4yv97QrEPvJG9nPRBKWGiAQsIA5J+WryX5NrfEfRPk0QQwyl16lpHlw6l0UMuk7S21xjQgyWo0MywfzoBWW7+t4HH9sqavvP4dYAw81BxXqVHQhefUOS23en4bFUPWE98pAN6bul+kS767vDK34yTC3lA2a8wLrBEilmFhdB74fxbAl+db91PivhwF/CR4Igxr35uLdof7+jAYyACopQzmsbHpvAAwT2lapLix8H03nztAC3fBqFSPBVdIv12lsrrDw4dfhJEzq7AbL/Y7L/nIcBsQ/3UyVnZk4kZP1KzyPCBLLIQNpCVgOLJzQuyaQ6k2QCBy0eJ0ppUyfp54LjwVg0X7bwncYbAomG4ZcFwTQnC2AX3oYG5n6Bz4SLLjxrFsY+v/SVa+GqH8uePBh1TPkHVNmzjXXymEf5jROlnd+EjfQdRyitkjPrg2HiQxxDcVhCh5J2L5+6CY9eIaYgrbd8zJnzAD8KnowHwh2bi4JLgmt7ktJ1XGizox7cWf3/Dod56KAcaIrSVw9XzYybdJCf0YRA6yrwPWXbwnzc/4+UDkmegi+AoCEMoue+cC7vnYVdmlbq/YLE/DWJX383oz2Ryq8anFrZ8jYvdoh8WI+dIugYL2SwRjmBoSwn56XIaot/QpMo3pYJIa4o8aZIZrjvB7BXO5aCDeMuZdUMT6AXGAGF1AeAWxFd2XIo1coR+OplMNDuYia8YAtnSTJ9JwGYWi2dJz3xrxsTQpBONf3yn8LVf8eH+o5eXc7lzCtHlDB+YyI8V9PyMsUPOeyvpB3rr9fDfNy263Zx33zTi5jldgP2OetUqGfbwl+0+zNYnrg64bluyIN/Awt1doDCQkCKpKXxuPaem/SyCHrKjg'
		EndIf
 
		Local $Symbol[] = ["jsmn_parse", "jsmn_init", "json_string_decode", "json_string_encode"]
		Local $CodeBase = _BinaryCall_Create($Code)
		If @error Then Exit MsgBox(16, "Json", "Startup Failure!")
 
		$SymbolList = _BinaryCall_SymbolList($CodeBase, $Symbol)
		If @error Then Exit MsgBox(16, "Json", "Startup Failure!")
	EndIf
	If $ProcName Then Return DllStructGetData($SymbolList, $ProcName)
EndFunc   ;==>__Jsmn_RuntimeLoader
 
Func Json_StringEncode($String, $Option = 0)
	Static $Json_StringEncode = __Jsmn_RuntimeLoader("json_string_encode")
	Local $Length = StringLen($String) * 6 + 1
	Local $Buffer = DllStructCreate("wchar[" & $Length & "]")
	Local $Ret = DllCallAddress("int:cdecl", $Json_StringEncode, "wstr", $String, "ptr", DllStructGetPtr($Buffer), "uint", $Length, "int", $Option)
	Return SetError($Ret[0], 0, DllStructGetData($Buffer, 1))
EndFunc   ;==>Json_StringEncode
 
Func Json_StringDecode($String)
	Static $Json_StringDecode = __Jsmn_RuntimeLoader("json_string_decode")
	Local $Length = StringLen($String) + 1
	Local $Buffer = DllStructCreate("wchar[" & $Length & "]")
	Local $Ret = DllCallAddress("int:cdecl", $Json_StringDecode, "wstr", $String, "ptr", DllStructGetPtr($Buffer), "uint", $Length)
	Return SetError($Ret[0], 0, DllStructGetData($Buffer, 1))
EndFunc   ;==>Json_StringDecode
 
Func Json_Decode($Json, $InitTokenCount = 1000)
	Static $Jsmn_Init = __Jsmn_RuntimeLoader("jsmn_init"), $Jsmn_Parse = __Jsmn_RuntimeLoader("jsmn_parse")
	If $Json = "" Then $Json = '""'
	Local $TokenList, $Ret
	Local $Parser = DllStructCreate("uint pos;int toknext;int toksuper")
	Do
		DllCallAddress("none:cdecl", $Jsmn_Init, "ptr", DllStructGetPtr($Parser))
		$TokenList = DllStructCreate("byte[" & ($InitTokenCount * 20) & "]")
		$Ret = DllCallAddress("int:cdecl", $Jsmn_Parse, "ptr", DllStructGetPtr($Parser), "wstr", $Json, "ptr", DllStructGetPtr($TokenList), "uint", $InitTokenCount)
		$InitTokenCount *= 2
	Until $Ret[0] <> $JSMN_ERROR_NOMEM
 
	Local $Next = 0
	Return SetError($Ret[0], 0, _Json_Token($Json, DllStructGetPtr($TokenList), $Next))
EndFunc   ;==>Json_Decode
 
Func _Json_Token(ByRef $Json, $Ptr, ByRef $Next)
	If $Next = -1 Then Return Null
 
	Local $Token = DllStructCreate("int;int;int;int", $Ptr + ($Next * 20))
	Local $Type = DllStructGetData($Token, 1)
	Local $Start = DllStructGetData($Token, 2)
	Local $End = DllStructGetData($Token, 3)
	Local $Size = DllStructGetData($Token, 4)
	$Next += 1
 
	If $Type = 0 And $Start = 0 And $End = 0 And $Size = 0 Then ; Null Item
		$Next = -1
		Return Null
	EndIf
 
	Switch $Type
		Case 0 ; Json_PRIMITIVE
			Local $Primitive = StringMid($Json, $Start + 1, $End - $Start)
			Switch $Primitive
				Case "true"
					Return True
				Case "false"
					Return False
				Case "null"
					Return Null
				Case Else
					If StringRegExp($Primitive, "^[+\-0-9]") Then
						Return Number($Primitive)
					Else
						Return Json_StringDecode($Primitive)
					EndIf
			EndSwitch
 
		Case 1 ; Json_OBJECT
			Local $Object = Json_ObjCreate()
			For $i = 0 To $Size - 1 Step 2
				Local $Key = _Json_Token($Json, $Ptr, $Next)
				Local $Value = _Json_Token($Json, $Ptr, $Next)
				If Not IsString($Key) Then $Key = Json_Encode($Key)
 
				If $Object.Exists($Key) Then $Object.Remove($Key)
				$Object.Add($Key, $Value)
			Next
			Return $Object
 
		Case 2 ; Json_ARRAY
			Local $Array[$Size]
			For $i = 0 To $Size - 1
				$Array[$i] = _Json_Token($Json, $Ptr, $Next)
			Next
			Return $Array
 
		Case 3 ; Json_STRING
			Return Json_StringDecode(StringMid($Json, $Start + 1, $End - $Start))
	EndSwitch
EndFunc   ;==>_Json_Token
 
Func Json_IsObject(ByRef $Object)
	Return (IsObj($Object) And ObjName($Object) = "Dictionary")
EndFunc   ;==>Json_IsObject
 
Func Json_IsNull(ByRef $Null)
	Return IsKeyword($Null) Or (Not IsObj($Null) And VarGetType($Null) = "Object")
EndFunc   ;==>Json_IsNull
 
Func Json_Encode_Compact($Data, $Option = 0)
	Local $Json = ""
 
	Select
		Case IsString($Data)
			Return '"' & Json_StringEncode($Data, $Option) & '"'
 
		Case IsNumber($Data)
			Return $Data
 
		Case IsArray($Data) And UBound($Data, 0) = 1
			$Json = "["
			For $i = 0 To UBound($Data) - 1
				$Json &= Json_Encode_Compact($Data[$i], $Option) & ","
			Next
			If StringRight($Json, 1) = "," Then $Json = StringTrimRight($Json, 1)
			Return $Json & "]"
 
		Case Json_IsObject($Data)
			$Json = "{"
			Local $Keys = $Data.Keys()
			For $i = 0 To UBound($Keys) - 1
				$Json &= '"' & Json_StringEncode($Keys[$i], $Option) & '":' & Json_Encode_Compact($Data.Item($Keys[$i]), $Option) & ","
			Next
			If StringRight($Json, 1) = "," Then $Json = StringTrimRight($Json, 1)
			Return $Json & "}"
 
		Case IsBool($Data)
			Return StringLower($Data)
 
		Case IsPtr($Data)
			Return Number($Data)
 
		Case IsBinary($Data)
			Return '"' & Json_StringEncode(BinaryToString($Data, 4), $Option) & '"'
 
		Case Else ; Keyword, DllStruct, Object
			Return "null"
	EndSelect
EndFunc   ;==>Json_Encode_Compact
 
Func Json_Encode_Pretty($Data, $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF = Default, $ObjectCRLF = Default, $NextIdent = "")
	Local $ThisIdent = $NextIdent, $Json = "", $String = "", $Match = "", $Keys = ""
	Local $Length = 0
 
	Select
		Case IsString($Data)
			$String = Json_StringEncode($Data, $Option)
			If BitAND($Option, $JSON_UNQUOTED_STRING) And Not BitAND($Option, $JSON_STRICT_PRINT) And Not StringRegExp($String, "[\s,:]") And Not StringRegExp($String, "^[+\-0-9]") Then
				Return $String
			Else
				Return '"' & $String & '"'
			EndIf
 
		Case IsArray($Data) And UBound($Data, 0) = 1
			If UBound($Data) = 0 Then Return "[]"
			If IsKeyword($ArrayCRLF) Then
				$ArrayCRLF = ""
				$Match = StringRegExp($ArraySep, "[\r\n]+$", 3)
				If IsArray($Match) Then $ArrayCRLF = $Match[0]
			EndIf
 
			If $ArrayCRLF Then $NextIdent &= $Indent
			$Length = UBound($Data) - 1
			For $i = 0 To $Length
				If $ArrayCRLF Then $Json &= $NextIdent
				$Json &= Json_Encode_Pretty($Data[$i], $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF, $ObjectCRLF, $NextIdent)
				If $i < $Length Then $Json &= $ArraySep
			Next
 
			If $ArrayCRLF Then Return "[" & $ArrayCRLF & $Json & $ArrayCRLF & $ThisIdent & "]"
			Return "[" & $Json & "]"
 
		Case Json_IsObject($Data)
			If $Data.Count = 0 Then Return "{}"
			If IsKeyword($ObjectCRLF) Then
				$ObjectCRLF = ""
				$Match = StringRegExp($ObjectSep, "[\r\n]+$", 3)
				If IsArray($Match) Then $ObjectCRLF = $Match[0]
			EndIf
 
			If $ObjectCRLF Then $NextIdent &= $Indent
			$Keys = $Data.Keys()
			$Length = UBound($Keys) - 1
			For $i = 0 To $Length
				If $ObjectCRLF Then $Json &= $NextIdent
				$Json &= Json_Encode_Pretty(String($Keys[$i]), $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep) & $ColonSep _
						 & Json_Encode_Pretty($Data.Item($Keys[$i]), $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep, $ArrayCRLF, $ObjectCRLF, $NextIdent)
				If $i < $Length Then $Json &= $ObjectSep
			Next
 
			If $ObjectCRLF Then Return "{" & $ObjectCRLF & $Json & $ObjectCRLF & $ThisIdent & "}"
			Return "{" & $Json & "}"
 
		Case Else
			Return Json_Encode_Compact($Data, $Option)
 
	EndSelect
EndFunc   ;==>Json_Encode_Pretty
 
Func Json_Encode($Data, $Option = 0, $Indent = Default, $ArraySep = Default, $ObjectSep = Default, $ColonSep = Default)
	If BitAND($Option, $JSON_PRETTY_PRINT) Then
		Local $Strict = BitAND($Option, $JSON_STRICT_PRINT)
 
		If IsKeyword($Indent) Then
			$Indent = @TAB
		Else
			$Indent = Json_StringDecode($Indent)
			If StringRegExp($Indent, "[^\t ]") Then $Indent = @TAB
		EndIf
 
		If IsKeyword($ArraySep) Then
			$ArraySep = "," & @CRLF
		Else
			$ArraySep = Json_StringDecode($ArraySep)
			If $ArraySep = "" Or StringRegExp($ArraySep, "[^\s,]|,.*,") Or ($Strict And Not StringRegExp($ArraySep, ",")) Then $ArraySep = "," & @CRLF
		EndIf
 
		If IsKeyword($ObjectSep) Then
			$ObjectSep = "," & @CRLF
		Else
			$ObjectSep = Json_StringDecode($ObjectSep)
			If $ObjectSep = "" Or StringRegExp($ObjectSep, "[^\s,]|,.*,") Or ($Strict And Not StringRegExp($ObjectSep, ",")) Then $ObjectSep = "," & @CRLF
		EndIf
 
		If IsKeyword($ColonSep) Then
			$ColonSep = ": "
		Else
			$ColonSep = Json_StringDecode($ColonSep)
			If $ColonSep = "" Or StringRegExp($ColonSep, "[^\s,:]|[,:].*[,:]") Or ($Strict And (StringRegExp($ColonSep, ",") Or Not StringRegExp($ColonSep, ":"))) Then $ColonSep = ": "
		EndIf
 
		Return Json_Encode_Pretty($Data, $Option, $Indent, $ArraySep, $ObjectSep, $ColonSep)
 
	ElseIf BitAND($Option, $JSON_UNQUOTED_STRING) Then
		Return Json_Encode_Pretty($Data, $Option, "", ",", ",", ":")
	Else
		Return Json_Encode_Compact($Data, $Option)
	EndIf
EndFunc   ;==>Json_Encode
 
Func Json_ObjCreate()
	Local $Object = ObjCreate('Scripting.Dictionary')
	$Object.CompareMode = 0
	Return $Object
EndFunc   ;==>Json_ObjCreate
 
Func Json_ObjPut(ByRef $Object, $Key, $Value)
	$Key = String($Key)
	If $Object.Exists($Key) Then $Object.Remove($Key)
	$Object.Add($Key, $Value)
EndFunc   ;==>Json_ObjPut
 
Func Json_ObjGet(ByRef $Object, $Key)
	Local $DynObject = $Object
	Local $Keys = StringSplit($Key, ".")
	For $x = 1 To $Keys[0]
		If $DynObject.Exists($Keys[$x]) Then
			If $x = $Keys[0] Then
				Return $DynObject.Item($Keys[$x])
			Else
				$DynObject = Json_ObjGet($DynObject, $Keys[$x])
			EndIf
		EndIf
	Next
	Return SetError(1, 0, '')
EndFunc   ;==>Json_ObjGet
 
Func Json_ObjDelete(ByRef $Object, $Key)
	$Key = String($Key)
	If $Object.Exists($Key) Then $Object.Remove($Key)
EndFunc   ;==>Json_ObjDelete
 
Func Json_ObjExists(ByRef $Object, $Key)
	Local $DynObject = $Object
	Local $Keys = StringSplit($Key, ".")
	For $x = 1 To $Keys[0]
		If $DynObject.Exists($Keys[$x]) Then
			If $x = $Keys[0] Then
				Return True
			Else
				$DynObject = Json_ObjGet($DynObject, $Keys[$x])
			EndIf
		Else
			Return False
		EndIf
	Next
	Return False
EndFunc   ;==>Json_ObjExists
 
Func Json_ObjGetCount(ByRef $Object)
	Return $Object.Count
EndFunc   ;==>Json_ObjGetCount
 
Func Json_ObjGetKeys(ByRef $Object)
	Return $Object.Keys()
EndFunc   ;==>Json_ObjGetKeys
 
Func Json_ObjGetItems(ByRef $Object)
	Return $Object.Items()
EndFunc   ;==>Json_ObjGetItems
 
Func Json_ObjClear(ByRef $Object)
	Return $Object.RemoveAll()
EndFunc   ;==>Json_ObjClear
 
; Both dot notation and square bracket notation can be supported
Func Json_Put(ByRef $Var, $Notation, $Data, $CheckExists = False)
	Local $Ret = 0, $Item = "", $Error = 0
	Local $Match = ""
 
	$Match = StringRegExp($Notation, "(^\[([^\]]+)\])|(^\.([^\.\[]+))", 3)
	If IsArray($Match) Then
		Local $Index
		If UBound($Match) = 4 Then
			$Index = String(Json_Decode($Match[3])) ; only string using dot notation
			$Notation = StringTrimLeft($Notation, StringLen($Match[2]))
		Else
			$Index = Json_Decode($Match[1])
			$Notation = StringTrimLeft($Notation, StringLen($Match[0]))
		EndIf
 
		If IsString($Index) Then
			If $CheckExists And (Not Json_IsObject($Var) Or Not Json_ObjExists($Var, $Index)) Then
				Return SetError(1, 0, False) ; no specific object
			EndIf
 
			If Not Json_IsObject($Var) Then $Var = Json_ObjCreate()
			If $Notation Then
				$Item = Json_ObjGet($Var, $Index)
				$Ret = Json_Put($Item, $Notation, $Data, $CheckExists)
				$Error = @error
				If Not $Error Then Json_ObjPut($Var, $Index, $Item)
				Return SetError($Error, 0, $Ret)
			Else
				Json_ObjPut($Var, $Index, $Data)
				Return True
			EndIf
 
		ElseIf IsInt($Index) Then
			If $Index < 0 Or ($CheckExists And (Not IsArray($Var) Or UBound($Var, 0) <> 1 Or $Index >= UBound($Var))) Then
				Return SetError(1, 0, False) ; no specific object
			EndIf
 
			If Not IsArray($Var) Or UBound($Var, 0) <> 1 Then
				Dim $Var[$Index + 1]
			ElseIf $Index >= UBound($Var) Then
				ReDim $Var[$Index + 1]
			EndIf
 
			If $Notation Then
				$Ret = Json_Put($Var[$Index], $Notation, $Data, $CheckExists)
				Return SetError(@error, 0, $Ret)
			Else
				$Var[$Index] = $Data
				Return True
			EndIf
 
		EndIf
	EndIf
	Return SetError(2, 0, False) ; invalid notation
EndFunc   ;==>Json_Put
 
; Both dot notation and square bracket notation can be supported
Func Json_Get(ByRef $Var, $Notation)
	Local $Match = StringRegExp($Notation, "(^\[([^\]]+)\])|(^\.([^\.\[]+))", 3)
	If IsArray($Match) Then
		Local $Index
		If UBound($Match) = 4 Then
			$Index = String(Json_Decode($Match[3])) ; only string using dot notation
			$Notation = StringTrimLeft($Notation, StringLen($Match[2]))
		Else
			$Index = Json_Decode($Match[1])
			$Notation = StringTrimLeft($Notation, StringLen($Match[0]))
		EndIf
 
		Local $Item
		If IsString($Index) And Json_IsObject($Var) And Json_ObjExists($Var, $Index) Then
			$Item = Json_ObjGet($Var, $Index)
		ElseIf IsInt($Index) And IsArray($Var) And UBound($Var, 0) = 1 And $Index >= 0 And $Index < UBound($Var) Then
			$Item = $Var[$Index]
		Else
			Return SetError(1, 0, "") ; no specific object
		EndIf
 
		If Not $Notation Then Return $Item
		Local $Ret = Json_Get($Item, $Notation)
		Return SetError(@error, 0, $Ret)
	EndIf
	Return SetError(2, 0, "") ; invalid notation
EndFunc   ;==>Json_Get
 
; List all JSON keys and their value to the Console
Func Json_Dump($Json, $InitTokenCount = 1000)
	Static $Jsmn_Init = __Jsmn_RuntimeLoader("jsmn_init"), $Jsmn_Parse = __Jsmn_RuntimeLoader("jsmn_parse")
	If $Json = "" Then $Json = '""'
	Local $TokenList, $Ret
	$Total_JSON_DUMP_Output = ""  ; reset totaldump variable at the start of the dump process (Use for testing)
	Local $Parser = DllStructCreate("uint pos;int toknext;int toksuper")
	Do
		DllCallAddress("none:cdecl", $Jsmn_Init, "ptr", DllStructGetPtr($Parser))
		$TokenList = DllStructCreate("byte[" & ($InitTokenCount * 20) & "]")
		$Ret = DllCallAddress("int:cdecl", $Jsmn_Parse, "ptr", DllStructGetPtr($Parser), "wstr", $Json, "ptr", DllStructGetPtr($TokenList), "uint", $InitTokenCount)
		$InitTokenCount *= 2
	Until $Ret[0] <> $JSMN_ERROR_NOMEM
 
	Local $Next = 0
	_Json_TokenDump($Json, DllStructGetPtr($TokenList), $Next)
EndFunc   ;==>Json_Dump
 
Func _Json_TokenDump(ByRef $Json, $Ptr, ByRef $Next, $ObjPath = "")
	If $Next = -1 Then Return Null
 
	Local $Token = DllStructCreate("int;int;int;int", $Ptr + ($Next * 20))
	Local $Type = DllStructGetData($Token, 1)
	Local $Start = DllStructGetData($Token, 2)
	Local $End = DllStructGetData($Token, 3)
	Local $Size = DllStructGetData($Token, 4)
	Local $Value
	$Next += 1
 
	If $Type = 0 And $Start = 0 And $End = 0 And $Size = 0 Then ; Null Item
		$Next = -1
		Return Null
	EndIf
 
	Switch $Type
		Case 0 ; Json_PRIMITIVE
			Local $Primitive = StringMid($Json, $Start + 1, $End - $Start)
			Switch $Primitive
				Case "true"
					Return "True"
				Case "false"
					Return "False"
				Case "null"
					Return "Null"
				Case Else
					If StringRegExp($Primitive, "^[+\-0-9]") Then
						Return Number($Primitive)
					Else
						Return Json_StringDecode($Primitive)
					EndIf
			EndSwitch
 
		Case 1 ; Json_OBJECT
			For $i = 0 To $Size - 1 Step 2
				Local $Key = _Json_TokenDump($Json, $Ptr, $Next)
				Local $cObjPath = $ObjPath & "." & $Key
				$Value = _Json_TokenDump($Json, $Ptr, $Next, $ObjPath & "." & $Key)
				If Not (IsBool($Value) And $Value = False) Then
					If Not IsString($Key) Then
						$Key = Json_Encode($Key)
					EndIf
					; show the key and its value
					ConsoleWrite("+-> " & $cObjPath & '  =' & $Value & @CRLF)
					$Total_JSON_DUMP_Output &= "+-> " & $cObjPath & '  =' & $Value & @CRLF
				EndIf
			Next
			Return False
		Case 2 ; Json_ARRAY
			Local $sObjPath = $ObjPath
			For $i = 0 To $Size - 1
				$sObjPath = $ObjPath & "[" & $i & "]"
				$Value = _Json_TokenDump($Json, $Ptr, $Next, $sObjPath)
				If Not (IsBool($Value) And $Value = False) Then ;XC - Changed line
					; show the key and its value
					ConsoleWrite("+=> " & $sObjPath & "=>" & $Value & @CRLF)
					$Total_JSON_DUMP_Output &= "+=> " & $sObjPath & "=>" & $Value & @CRLF
				EndIf
			Next
			$ObjPath = $sObjPath
			Return False
 
		Case 3 ; Json_STRING
			Local $LastKey = Json_StringDecode(StringMid($Json, $Start + 1, $End - $Start))
			Return $LastKey
	EndSwitch
EndFunc   ;==>_Json_TokenDump