' !! this file should not be modified

' DRPF file is in the following format:
' REMOTEINFO
' LICENCEINFO (however many are needed)
' ALGCHANGE (however many are needed)
' DATACHANGE blocks (however many are needed) - may refer to external files.

' Remember: you only need to specify a value if you want to change it. Otherwise you
' specify the default "no change" setting. For most entries this is indicated by 0.
' However, for MaxNetUsers and LicenceNetUsers it is "-2" because 0 is a valid value to change to.

Imports System
Imports System.Runtime.InteropServices     ' so we can marshal our structures as a block of memory
Imports System.Text                        ' for StringBuilder

' ***********  define structures that make up the DRPF file   *******************

Module drpf_code

	<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
	 Structure REMOTEINFO
		Dim Header1 As Byte                ' Should be set to "DRPF" = DinkeyRemote Parameter File
		Dim Header2 As Byte
		Dim Header3 As Byte
		Dim Header4 As Byte
		Dim Version As Integer             ' version of this structure - currently 2
        <VBFixedString(12), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> _
        Dim ProdCode As String             ' Product Code
		Dim DongleNumber As UInt32	
		Dim UpdateNumber As Integer
        <VBFixedString(256), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> _
        Dim Notes As String                ' user-defined notes	
		Dim NumLicences As Integer
		Dim NumAlgs As Integer             ' number of algs to set (incl r/w alg)
		Dim NumDataChanges As Integer      ' number of data change blocks specified	
		Dim DataAreaSize As Integer        ' -1 = increase as necessary. specifies the new size of the data area
		Dim PerLicence As Integer          ' 0 = no change, 1 = per licence net users, 2 = per product net users
		Dim MaxNetUsers As Integer         ' product net users, unlimited = -1, no change = -2
		Dim LastUsedDay As Integer         ' Day = 0 (only) -> no change
		Dim LastUsedMonth As Integer
		Dim LastUsedYear As Integer
        <VBFixedString(12), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=12)> _
        Dim DataEncKeyProdCode As String       
        Dim ChangeCodeType As Integer      ' 0 = no change, bit0 = only accept secure codes, bit1 = accept secure & short codes
		Dim CodeType As Integer            ' type of code to generate. 0 = secure code, 1 = short code	
        <VBFixedString(256), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> _
        Dim OutputFileName As String       ' name of file to output code to. Null (default) -> UpdateCode.ducf
		Dim LockDongle As Integer          ' 1 = lock dongle to user's computer
		Dim ResetDongleLock As Integer     ' 1 = reset the dongle lock so that the dongle can be locked to a new machine
    End Structure

	<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
	 Structure LICENCEINFO
        <VBFixedString(256), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> _
        Dim LicenceName As String          ' licence name       	
		Dim Action As Integer              ' 0 = change, 1 = delete, 2 = add
		Dim AddSetExecs As Integer         ' 0 = no change to execs, 1 = add, 2 = set
		Dim Execs As Integer               ' -1 = no limit
		Dim ExecsWarn As Integer           ' 0 = remove warning, -1 = no change
		Dim ExpiryDay As Integer           ' -1 = no limit, 0 = no change
		Dim ExpiryMonth As Integer
		Dim ExpiryYear As Integer
		Dim AddSetMaxDays As Integer       ' 0 = no change, 1 = add, 2 = set
		Dim MaxDays As Integer             ' -1 = no limit
		Dim DaysWarn As Integer            ' 0 = remove warning, -1 = no change
		Dim FeaturesFlag As Integer        ' 0 = don't change features, 1 = change features
		Dim Features As UInt32             ' all values OK
		Dim LicenceNetUsers As Integer        ' unlimited = -1,  no change = -2
	End Structure
	
	<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
	 Structure ALGCHANGE
		Dim alg_number As Integer          ' number of algorithm to set (0 = r/w algorithm)
        <VBFixedArray(12), MarshalAs(UnmanagedType.ByValArray, SizeConst:=12)> _
		Dim alg As Byte()                  ' coded form of algorithm
	End Structure

	<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
	 Structure DATACHANGE
		Dim type As Integer                ' 1 = file, 2 = hex data, 3 = ascii, 4 = ascii null-term string
		Dim offset As Integer              ' offset in dongle data memory to write data
		Dim length As Integer              ' length of data to change 
        <VBFixedArray(256), MarshalAs(UnmanagedType.ByValArray, SizeConst:=256)> _
		Dim data As Byte()                 ' data to write OR name of data file depending on which method set
	End Structure

	<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
	 Structure UPDATE_CODE_OUTPUTS
        <VBFixedString(128*3), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128*3)> _
        Dim ShortCode As String            ' the update code (for short update codes only)       	
        <VBFixedString(256), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> _
        Dim UpdateCodeFile As String       ' the output ducf that contains the update code       	
        <VBFixedString(8), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=8)> _
        Dim ConfirmationCode As String      ' the confirmation code       	
    End Structure

' **********************  DinkeyRemote Module Imports  ************************

    ' Loads DRPF from disk and returns handle
    Declare Ansi Function LoadDRPFFromFile32 Lib "DinkeyRemote32.dll" Alias "LoadDRPFFromFile" (ByVal drpf_file As String, ByRef err_code As Integer) As IntPtr
    Declare Ansi Function LoadDRPFFromFile64 Lib "DinkeyRemote64.dll" Alias "LoadDRPFFromFile" (ByVal drpf_file As String, ByRef err_code As Integer) As IntPtr

    ' Loads DRPF from disk into structures supplied (so we can modify DRPF in memory)
    Declare Ansi Function ReadDRPFEx32 Lib "DinkeyRemote32.dll" Alias "ReadDRPFEx" (ByVal drpf_file As String, ByRef MyRemoteInfo As REMOTEINFO, ByVal licence_bytes() As Byte, _
        ByVal algchange_bytes() As Byte, ByVal datachange_bytes() As Byte, ByRef drpf_version As Integer, ByRef drpf_format As Integer) As Integer
    Declare Ansi Function ReadDRPFEx64 Lib "DinkeyRemote64.dll" Alias "ReadDRPFEx" (ByVal drpf_file As String, ByRef MyRemoteInfo As REMOTEINFO, ByVal licence_bytes() As Byte, _
        ByVal algchange_bytes() As Byte, ByVal datachange_bytes() As Byte, ByRef drpf_version As Integer, ByRef drpf_format As Integer) As Integer

    ' Loads DRPF from structures and returns a handle
    Declare Ansi Function LoadDRPF32 Lib "DinkeyRemote32.dll" Alias "LoadDRPF" (ByRef MyRemoteInfo As REMOTEINFO, ByVal licence_bytes() As Byte, ByVal algchange_bytes() As Byte, _
        ByVal datachange_bytes() As Byte, ByRef ret_code As Integer) As IntPtr
    Declare Ansi Function LoadDRPF64 Lib "DinkeyRemote64.dll" Alias "LoadDRPF" (ByRef MyRemoteInfo As REMOTEINFO, ByVal licence_bytes() As Byte, ByVal algchange_bytes() As Byte, _
        ByVal datachange_bytes() As Byte, ByRef ret_code As Integer) As IntPtr
        
    ' saves the DRPF file to disk
    Declare Ansi Function WriteDRPFEx32 Lib "DinkeyRemote32.dll" Alias "WriteDRPFEx" (ByVal drpf_handle As IntPtr, ByVal drpf_file As String, ByVal drpf_format As Integer, _
        ByVal drpf_version As Integer, ByVal flags As Integer) As Integer
    Declare Ansi Function WriteDRPFEx64 Lib "DinkeyRemote64.dll" Alias "WriteDRPFEx" (ByVal drpf_handle As IntPtr, ByVal drpf_file As String, ByVal drpf_format As Integer, _
        ByVal drpf_version As Integer, ByVal flags As Integer) As Integer
     
    ' unloads the DRPF file
    Declare Ansi Sub UnloadDRPF32 Lib "DinkeyRemote32.dll" Alias "UnloadDRPF" (ByRef drpf_handle As IntPtr)
    Declare Ansi Sub UnloadDRPF64 Lib "DinkeyRemote64.dll" Alias "UnloadDRPF" (ByRef drpf_handle As IntPtr)
    
    'main functions
    Declare Ansi Function GenerateUpdateCode32 Lib "DinkeyRemote32.dll" Alias "GenerateUpdateCode" (ByVal drpf_handle As IntPtr, ByVal dongle_number As UInt32, _
        ByVal update_number As Integer, ByVal ducf_file As String, ByVal logfile As String, ByRef Outputs As UPDATE_CODE_OUTPUTS) As Integer
    Declare Ansi Function GenerateUpdateCode64 Lib "DinkeyRemote64.dll" Alias "GenerateUpdateCode" (ByVal drpf_handle As IntPtr, ByVal dongle_number As UInt32, _
        ByVal update_number As Integer, ByVal ducf_file As String, ByVal logfile As String, ByRef Outputs As UPDATE_CODE_OUTPUTS) As Integer
        
    ' get the message output by DinkeyRemote
    Declare Ansi Sub GetLastMessage32 Lib "DinkeyRemote32.dll" Alias "GetLastMessage" (ByVal message As StringBuilder, ByVal buffer_size As Integer, ByRef message_length As Integer)
    Declare Ansi Sub GetLastMessage64 Lib "DinkeyRemote64.dll" Alias "GetLastMessage" (ByVal message As StringBuilder, ByVal buffer_size As Integer, ByRef message_length As Integer)

    ' get the extended error (set if an error occurs)
    Declare Ansi Function GetLastExtendedError32 Lib "DinkeyRemote32.dll" Alias "GetLastExtendedError" () As Integer
    Declare Ansi Function GetLastExtendedError64 Lib "DinkeyRemote64.dll" Alias "GetLastExtendedError" () As Integer

    ' **********************  Constants  ************************
    Public Const DRPF_VERSION As Integer = 2  ' current DRPF version

    ' WriteDRPFEx API flags
    Public Const FLAGS_LOSE_FEATURES As Integer = 1     ' means that you can save the DRPF file to an earlier version even if it means losing some new features that have been currently set

    ' constants for drpf_format in ReadDRPFEx and WriteDRPFEx functions
    Public Const DRPF_FORMAT_BINARY As Integer = 1
    Public Const DRPF_FORMAT_JSON As Integer = 2
    Public Const DRPF_FORMAT_JSON_MIN As Integer = 3    ' this value is only used with WriteDRPEx. ReadDRPFEx cannot return this value.
    
    ' private constants
    Private const MAX_LICENCES As Integer = 255         ' max number of licence changes in a DRPF file (theoretically unlimited but easier if we limit it)
    Private Const MAX_ALG_CHANGES As Integer = 21       ' max number of alg changes in DRPF file
    Private Const MAX_DATA_CHANGES As Integer = 127     ' max number of data area changes in DRPF file

    ' **********************  API Functions  ************************
    ' We only want to load the correct DLL for the bit-ness of the computer. The other DLL may not exist.
    ' if the appropriate DLL does not exist in the same directory as this test program you will receive a DllNotFoundException

    ' call the correct dll depending on whether we are running 32-bit or 64-bit code
    Function LoadDRPFFromFile(ByVal drpf_file As String, ByRef err_code As Integer) As IntPtr
        If (IntPtr.Size = 4) Then
            LoadDRPFFromFile = LoadDRPFFromFile32(drpf_file, err_code)
        Else
            LoadDRPFFromFile = LoadDRPFFromFile64(drpf_file, err_code)
        End If
    End Function
    
    ' we have to do some marshalling of arrays of bytes into arrays of structures
    Function ReadDRPFEx(ByVal drpf_file As String, ByRef MyRemoteInfo As REMOTEINFO, ByRef LicenceInfoArray() As LICENCEINFO, ByRef AlgChangesArray() As ALGCHANGE, _
            ByRef DataChangesArray() As DATACHANGE, ByRef drpf_version As Integer, ByRef drpf_format As Integer) As Integer
        Dim i As Integer
        Dim MyLicenceInfo As New LICENCEINFO
        Dim licence_bytes(MAX_LICENCES * Marshal.SizeOf(MyLicenceInfo)) As Byte
        Dim licence_bytes_one_struct(Marshal.SizeOf(MyLicenceInfo)) As Byte
        Dim MyAlgChanges As New ALGCHANGE
        Dim algchange_bytes(MAX_ALG_CHANGES * Marshal.SizeOf(MyAlgChanges)) As Byte
        Dim algchange_bytes_one_struct(Marshal.SizeOf(MyAlgChanges)) As Byte
        Dim MyDataChanges As New DATACHANGE
        Dim datachange_bytes(MAX_DATA_CHANGES * Marshal.SizeOf(MyDataChanges)) As Byte
        Dim datachange_bytes_one_struct(Marshal.SizeOf(MyDataChanges)) As Byte
        Dim MyGC As GCHandle
        MyRemoteInfo = New REMOTEINFO

        If (IntPtr.Size = 4) Then
            ReadDRPFEx = ReadDRPFEx32(drpf_file, MyRemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, drpf_version, drpf_format)
        Else
            ReadDRPFEx = ReadDRPFEx64(drpf_file, MyRemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, drpf_version, drpf_format)
        End If

        If ReadDRPFEx <> 0 Then
            Exit Function
        End If

        ' convert byte array to an array of structs
        ReDim LicenceInfoArray(MyRemoteInfo.NumLicences)
        For i = 0 To MyRemoteInfo.NumLicences - 1
            LicenceInfoArray(i) = New LICENCEINFO
            Array.Copy(licence_bytes, i * Marshal.SizeOf(MyLicenceInfo.GetType), licence_bytes_one_struct, 0, Marshal.SizeOf(MyLicenceInfo.GetType))
            MyGC = GCHandle.Alloc(licence_bytes_one_struct, GCHandleType.Pinned)
            LicenceInfoArray(i) = Marshal.PtrToStructure(MyGC.AddrOfPinnedObject, MyLicenceInfo.GetType)
            MyGC.Free()
        Next

        ReDim AlgChangesArray(MyRemoteInfo.NumAlgs)
        For i = 0 To MyRemoteInfo.NumAlgs - 1
            AlgChangesArray(i) = New ALGCHANGE
            Array.Copy(algchange_bytes, i * Marshal.SizeOf(MyAlgChanges.GetType), algchange_bytes_one_struct, 0, Marshal.SizeOf(MyAlgChanges.GetType))
            MyGC = GCHandle.Alloc(algchange_bytes_one_struct, GCHandleType.Pinned)
            AlgChangesArray(i) = Marshal.PtrToStructure(MyGC.AddrOfPinnedObject, MyAlgChanges.GetType)
            MyGC.Free()
        Next

        ReDim DataChangesArray(MyRemoteInfo.NumDataChanges)
        For i = 0 To MyRemoteInfo.NumDataChanges - 1
            DataChangesArray(i) = New DATACHANGE
            Array.Copy(datachange_bytes, i * Marshal.SizeOf(MyDataChanges.GetType), datachange_bytes_one_struct, 0, Marshal.SizeOf(MyDataChanges.GetType))
            MyGC = GCHandle.Alloc(datachange_bytes_one_struct, GCHandleType.Pinned)
            DataChangesArray(i) = Marshal.PtrToStructure(MyGC.AddrOfPinnedObject, MyDataChanges.GetType)
            MyGC.Free()
        Next

    End Function

    Function LoadDRPF(ByRef MyRemoteInfo As REMOTEINFO, ByRef LicenceInfoArray() As LICENCEINFO, ByRef AlgChangesArray() As ALGCHANGE, ByRef DataChangesArray() As DATACHANGE, _
            ByRef ret_code As Integer) As IntPtr
        Dim i As Integer
        Dim MyLicenceInfo As New LICENCEINFO
        Dim licence_bytes(MAX_LICENCES * Marshal.SizeOf(MyLicenceInfo)) As Byte
        Dim licence_bytes_one_struct(Marshal.SizeOf(MyLicenceInfo)) As Byte
        Dim MyAlgChanges As New ALGCHANGE
        Dim algchange_bytes(MAX_ALG_CHANGES * Marshal.SizeOf(MyAlgChanges)) As Byte
        Dim algchange_bytes_one_struct(Marshal.SizeOf(MyAlgChanges)) As Byte
        Dim MyDataChanges As New DATACHANGE
        Dim datachange_bytes(MAX_DATA_CHANGES * Marshal.SizeOf(MyDataChanges)) As Byte
        Dim datachange_bytes_one_struct(Marshal.SizeOf(MyDataChanges)) As Byte
        Dim MyGC As GCHandle

        ' convert array of structs to byte arrays
        For i = 0 To MyRemoteInfo.NumLicences - 1
            MyGC = GCHandle.Alloc(licence_bytes_one_struct, GCHandleType.Pinned)
            Marshal.StructureToPtr(LicenceInfoArray(i), MyGC.AddrOfPinnedObject, False)
            MyGC.Free()
            Array.Copy(licence_bytes_one_struct, 0, licence_bytes, i * Marshal.SizeOf(MyLicenceInfo.GetType), Marshal.SizeOf(MyLicenceInfo.GetType))
        Next

        For i = 0 To MyRemoteInfo.NumAlgs - 1
            MyGC = GCHandle.Alloc(algchange_bytes_one_struct, GCHandleType.Pinned)
            Marshal.StructureToPtr(AlgChangesArray(i), MyGC.AddrOfPinnedObject, False)
            MyGC.Free()
            Array.Copy(algchange_bytes_one_struct, 0, algchange_bytes, i * Marshal.SizeOf(MyAlgChanges.GetType), Marshal.SizeOf(MyAlgChanges.GetType))
        Next

        For i = 0 To MyRemoteInfo.NumDataChanges - 1
            MyGC = GCHandle.Alloc(datachange_bytes_one_struct, GCHandleType.Pinned)
            Marshal.StructureToPtr(DataChangesArray(i), MyGC.AddrOfPinnedObject, False)
            MyGC.Free()
            Array.Copy(datachange_bytes_one_struct, 0, datachange_bytes, i * Marshal.SizeOf(MyDataChanges.GetType), Marshal.SizeOf(MyDataChanges.GetType))
        Next

        If (IntPtr.Size = 4) Then
            LoadDRPF = LoadDRPF32(MyRemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, ret_code)
        Else
            LoadDRPF = LoadDRPF64(MyRemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, ret_code)
        End If

    End Function

    Function WriteDRPFEx(ByVal drpf_handle As IntPtr, ByVal drpf_file As String, ByVal drpf_format As Integer, ByVal drpf_version As Integer, ByVal flags As Integer) As Integer
        If (IntPtr.Size = 4) Then
            WriteDRPFEx = WriteDRPFEx32(drpf_handle, drpf_file, drpf_format, drpf_version, flags)
        Else
            WriteDRPFEx = WriteDRPFEx64(drpf_handle, drpf_file, drpf_format, drpf_version, flags)
        End If
    End Function

    Sub UnloadDRPF(ByRef drpf_handle As IntPtr)
        If (IntPtr.Size = 4) Then
            UnloadDRPF32(drpf_handle)
        Else
            UnloadDRPF64(drpf_handle)
        End If
    End Sub
  
    Function GenerateUpdateCode(ByVal drpf_handle As IntPtr, ByVal dongle_number As UInt32, ByVal update_number As Integer, ByVal ducf_file As String, _
            ByVal logfile As String, ByRef Outputs As UPDATE_CODE_OUTPUTS) As Integer
        If (IntPtr.Size = 4) Then
            GenerateUpdateCode = GenerateUpdateCode32(drpf_handle, dongle_number, update_number, ducf_file, logfile, Outputs)
        Else
            GenerateUpdateCode = GenerateUpdateCode64(drpf_handle, dongle_number, update_number, ducf_file, logfile, Outputs)
        End If
    End Function

    ' do a bit of work here to make the function easier to use for VB.NET
    Function GetLastMessage() As String
        Dim length, dummy As Integer
        Dim message As StringBuilder

        If (IntPtr.Size = 4) Then
            GetLastMessage32(Nothing, 0, length)
            message = New StringBuilder(length)
            GetLastMessage32(message, length, dummy)
        Else
            GetLastMessage64(Nothing, 0, length)
            message = New StringBuilder(length)
            GetLastMessage64(message, length, dummy)
        End If

        Return message.ToString()
    End Function

    Function GetLastExtendedError() As Integer
        If (IntPtr.Size = 4) Then
            GetLastExtendedError = GetLastExtendedError32()
        Else
            GetLastExtendedError = GetLastExtendedError64()
        End If
    End Function


End Module
