;AssembleIt by Andy @ www.autoit.de
;
;see examples how to call _AssembleIt()

#include-once
#include <FASM.au3>


If @AutoItX64 Then
    MsgBox(0, "_AssembleIt Error", "Sorry, 64Bit is not supported. Program will be terminated!")
    Exit
EndIf

Global $_ASSEMBLEIT_FLAG = 1
Global $Fasm = FasmInit()
If @error Then
    MsgBox(0, "_AssembleIt Error", "Not able to FasmInit! Program will be terminated!")
    Exit
EndIf

; #FUNCTION# ======================================================================================
; Name ..........: _AssembleIt()
; Description ...: "Wrapper" for the FASM.au3 by Ward
; Syntax ........: _AssembleIt($Returntype, $sFunc, $Type1 = "type", $Param1 = 0, $Type2 = "type", $Param2 = 0....
; Parameters ....: $Returntype          - Data type returned by the assembled program
;                  $sFunc               - Name of the function, in which the Assemblercode is contained
;                  $sType1              - DataType of Parameter1
;                  $sParam1             - Parameter1
;                  $sType2              - DataType of Parameter2
;                  $sParam2             - Parameter2.....and so on, you can pass up to 20 parameters
;
; Return values .: Success    depends on $Returntype    @error=0
;                  Failure    @error = -2   FasmReset has failed
;                  Failure    @error = -3   Error in Assemblercode detected by Fasm
;                  Failure    @error = -4   Function with Assemblercode doesn´t exist (i.e. wrong functionname)
;                  Failure    @error = -5   Error while executing MemoryFuncCall
;
; Author ........: Andy @ www.autoit.de
; Modified ......:
; Remarks .......: _AssembleIt() instructs MemoryFuncCall with cdecl-convention, so only a RET is necessary at the end of the ASM-code
;                  If $_ASSEMBLEIT_FLAG = 0 is set before calling AssembleIt(), an AutoIt-code to call the opcodes without the need of FASM.au3 is created
; Related .......: Fasm.au3 by Ward  http://www.autoitscript.com/forum/index.php?showtopic=111613&view=findpost&p=782727
; Link ..........:
; Example .......:
; =================================================================================================

Func _AssembleIt($Returntype, $sFunc, $Type1 = "int", $Param1 = 0, $Type2 = "int", $Param2 = 0, $Type3 = "int", $Param3 = 0, $Type4 = "int", $Param4 = 0, $Type5 = "", $Param5 = 0, $Type6 = "", $Param6 = 0, $Type7 = "", $Param7 = 0, $Type8 = "", $Param8 = 0, $Type9 = "", $Param9 = 0, $Type10 = "", $Param10 = 0, $Type11 = "", $Param11 = 0, $Type12 = "", $Param12 = 0, $Type13 = "", $Param13 = 0, $Type14 = "", $Param14 = 0, $Type15 = "", $Param15 = 0, $Type16 = "", $Param16 = 0, $Type17 = "", $Param17 = 0, $Type18 = "", $Param18 = 0, $Type19 = "", $Param19 = 0, $Type20 = "", $Param20 = 0) ;assembles the code
    FasmReset($Fasm)
    If @error Then
        MsgBox(0, "_AssembleIt Error", "Error in Function FasmReset()")
        Return SetError(-2, 0, "ERROR -2")
    EndIf
    If $sFunc <> "" Then Call($sFunc)                           ;extract Assemblercode from function $sFunc()
    If @error = 0xDEAD Then
        MsgBox(0, "_AssembleIt Error", "The called function " & $sFunc & " doesn´t exist or contains errors!")
        Return SetError(-4, 0, "ERROR -4")
    EndIf
    Local $bytecode = FasmGetBinary($Fasm)                      ;assemble ASM-code to opcodes
    If @extended Then                                           ;shows errors during assembling
        Local $Error = FasmGetLastError()                       ;gets errors
        MsgBox(0, "FASM-ERROR in function " & $sFunc & "()", "Error Code:" & $Error[0] & _
                @CRLF & "Error Message:" & $Error[1] & @CRLF & "Error Line:" & $Error[2] & @CRLF)
        Return SetError(-3, 0, "ERROR -3")
    Else                                                        ;no errors during assembling the code
        FileDelete("asm_test.bin")                              ;creates binary file with opcodes, in the case that someone wants to use an external debugger^^
        FileWrite("asm_test.bin", BinaryToString(String(FasmGetBinary($Fasm))))
        ConsoleWrite($bytecode & @CRLF)                         ;opcodes, can easily be copied and inserted somewhere....
        If $_ASSEMBLEIT_FLAG = 0 Then                           ;if less then 4 parameters, CallWindowProcW is possible
            If @NumParams > 10 Then                             ;only a maximum of 4 parameters in CallWindowProcW posssible
                MsgBox(0, "_AssembleIt Error", "The $_ASSEMBLEIT_FLAG is set to 0, but more than 4 Parameters are used in the Function " & $sFunc & @CRLF & _
                        "Please reduce the number of parameters to a maximum of 4 if you want an AutoItscript with a CallWindowProcW-call!")
                Exit
            Else                                                ;all is ready to create an AutoItscript which can execute the opcodes without FASM.au3
                Local $scriptstring = 'Global $tCodeBuffer = DllStructCreate("byte[' & StringLen($bytecode) / 2 - 1 & ']") ;reserve Memory for opcodes' & @CRLF & _
                        'DllStructSetData($tCodeBuffer, 1,"' & $bytecode & '") ;write opcodes into memory' & @CRLF
                $scriptstring &= '$ret=DllCall("user32.dll", "' & $Returntype & '",' & ' "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer)'
                Local $n = 1
                For $i = 3 To 9 Step 2                          ;CallWindowProcW must be called with 4 parameters...
                    $scriptstring &= ',"' & Eval("Type" & $n) & '",'
                    If Eval("Param" & $n) <> 0 Or Eval("Param" & $n) <> "" Then
                        $scriptstring &= "Param" & $n
                    Else
                        $scriptstring &= '0'
                    EndIf
                    $n += 1
                Next
                $scriptstring &= ')' & @CRLF
                ClipPut($scriptstring)                          ;puts the AutoItcode into Clipboard
                MsgBox(0, "_AssembleIt() Info!", "The following code was created and written into the Clipboard:" & _
                        @CRLF & @CRLF & $scriptstring & @CRLF & @CRLF & @CRLF & _
                        "This code can now be inserted into an AutoIt-Script, please adapt the parameters in the Dll-call to the used AutoIt-variables!" & _
                        @CRLF & "The Program will be terminated!")
                FasmExit($Fasm)
                Exit
            EndIf
        EndIf

        ;MemoryFuncCall
        Local $scriptstring = 'MemoryFuncCall("' & $Returntype & ':cdecl",' & FasmGetFuncPtr($Fasm) ;cdecl instructs the function to clean the stack, only a simple RET at the end is necessary
        ;Local $scriptstring = 'MemoryFuncCall("' & $Returntype & '",' & FasmGetFuncPtr($Fasm) ;if "compatible" mode to existing programs is required, please commend out this line
        Local $n = 1
        For $i = 3 To @NumParams Step 2                         ;all parameters
            $scriptstring &= ',"' & Eval("Type" & $n) & '", $Param' & $n
            $n += 1
        Next
        $scriptstring &= ')'
        Local $a = Execute($scriptstring)                       ;do the MemoryFuncCall, execute the opcodes
        If @error Then
            MsgBox(0, "_AssembleIt Error", "Érror executing the MemoryFuncCall!")
            Return SetError(-5, 0, "ERROR -5")
        EndIf
        ;_arraydisplay($a)
        Return SetError(0, 0, $a[0])
    EndIf
EndFunc                                                         ;==>_AssembleIt

Func _($str)                                                    ;short version of Fasmadd
    Fasmadd($Fasm, $str)
EndFunc                                                         ;==>_














