// !! 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 (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.

using System;
using System.Runtime.InteropServices;   // so we can marshal the structures as a block of memory
using System.Text;						// for StringBuilder class for GetLastMessage API

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

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
public struct LICENCEINFO
{
	[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
	public string LicenceName;          // name of the licence
	public int Action;                  // 0 = change, 1 = delete, 2 = add
	public int AddSetExecs;             // 0 = no change to execs, 1 = add, 2 = set
	public int Execs;                   // -1 = no limit
	public int ExecsWarn;               // 0 = remove warning, -1 = no change
	public int ExpiryDay;               // -1 = no limit, 0 = no change
	public int ExpiryMonth;
	public int ExpiryYear;
	public int AddSetMaxDays;           // 0 = no change, 1 = add, 2 = set
	public int MaxDays;                 // -1 = no limit
	public int DaysWarn;                // 0 = remove warning, -1 = no change
	public int FeaturesFlag;            // 0 = don't change features, 1 = change features
	public uint	Features;               // all values OK
	public int LicenceNetUsers;         // unlimited = -1,  no change = -2
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
public struct ALGCHANGE
{				
	public int alg_number;              // number of algorithm to set (0 = r/w algorithm)
	[MarshalAs(UnmanagedType.ByValArray, SizeConst=12)]
	public byte[] alg;					// algorithm 
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
public struct DATACHANGE
{
	public int type;                    // 1 = file, 2 = hex data, 3 = ascii, 4 = ascii null-term string
	public int offset;                  // offset in dongle data memory to write data
	public int length;                  // length of data to change 
	[MarshalAs(UnmanagedType.ByValArray, SizeConst=256)]
	public byte[] data;                 // data to write OR name of data file depending on which method set
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
public struct UPDATE_CODE_OUTPUTS
{
	[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128*3)]
	public string ShortCode;            // the update code (for short update codes only)
	[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
    public string UpdateCodeFile;       // the output ducf that contains the update code 
	[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)]   
    public string ConfirmationCode;     // the confirmation code
}



// **********************  API functions  ************************
// we declare classes for all the API functions for each bitness

// Loads DRPF from disk and returns handle
class LoadDRPFFromFile32
{
    [DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
    public static extern IntPtr LoadDRPFFromFile(string drpf_file, out int err_code);
}

class LoadDRPFFromFile64
{
    [DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
    public static extern IntPtr LoadDRPFFromFile(string drpf_file, out int err_code);
}

// Loads DRPF from disk into structures supplied (so we can modify DRPF in memory)
class ReadDRPFEx32
{
    [DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
    public static extern int ReadDRPFEx(string drpf_file, out REMOTEINFO RemoteInfo, byte[] licence_bytes, byte[] algchange_bytes, byte[] datachange_bytes, out int drpf_version, out int drpf_format);
}

class ReadDRPFEx64
{
    [DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
    public static extern int ReadDRPFEx(string drpf_file, out REMOTEINFO RemoteInfo, byte[] licence_bytes, byte[] algchange_bytes, byte[] datachange_bytes, out int drpf_version, out int drpf_format);
}

// Loads DRPF from structures and returns a handle
class LoadDRPF32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern IntPtr LoadDRPF(ref REMOTEINFO RemoteInfo, byte[] licence_bytes, byte[] algchange_bytes, byte[] datachange_bytes, out int ret_code);
}

class LoadDRPF64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern IntPtr LoadDRPF(ref REMOTEINFO RemoteInfo, byte[] licence_bytes, byte[] algchange_bytes, byte[] datachange_bytes, out int ret_code);
}

// saves the DRPF file to disk
class WriteDRPFEx32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern int WriteDRPFEx(IntPtr drpf_handle, string drpf_file, int drpf_format, int drpf_version, int flags);
}

class WriteDRPFEx64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern int WriteDRPFEx(IntPtr drpf_handle, string drpf_file, int drpf_format, int drpf_version, int flags);
}

// unloads the DRPF file
class UnloadDRPF32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern void UnloadDRPF(ref IntPtr drpf_handle);
}

class UnloadDRPF64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern void UnloadDRPF(ref IntPtr drpf_handle);
}

class GenerateUpdateCode32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern int GenerateUpdateCode(IntPtr drpf_handle, uint dongle_number, int update_number, string ducf_file, string logfile, out UPDATE_CODE_OUTPUTS Outputs);
}

class GenerateUpdateCode64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern int GenerateUpdateCode(IntPtr drpf_handle, uint dongle_number, int update_number, string ducf_file, string logfile, out UPDATE_CODE_OUTPUTS Outputs);
}

class GetLastMessage32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern void GetLastMessage(StringBuilder message, int buffer_size, out int message_length);
}

class GetLastMessage64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern void GetLastMessage(StringBuilder message, int buffer_size, out int message_length);
}

class GetLastExtendedError32
{
	[DllImport("DinkeyRemote32.dll", CharSet = CharSet.Ansi)]
	public static extern int GetLastExtendedError();
}

class GetLastExtendedError64
{
	[DllImport("DinkeyRemote64.dll", CharSet = CharSet.Ansi)]
	public static extern int GetLastExtendedError();
}

// ********************* DinkeyRemote class **********************
class DinkeyRemote
{
	// constants
	public const int DRPF_VERSION = 2;					// current DRPF version

	// WriteDRPFEx API flags
	public const int FLAGS_LOSE_FEATURES = 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 int DRPF_FORMAT_BINARY = 1;
	public const int DRPF_FORMAT_JSON = 2;
	public const int DRPF_FORMAT_JSON_MIN = 3;			// this value is only used with WriteDRPFEx. ReadDRPFEx cannot return this value.    
    
	// private constants
	private const int MAX_LICENCES = 255;				// max number of licence changes in a DRPF file (theoretically unlimited but easier if we limit it)
	private const int MAX_ALG_CHANGES = 21;				// max number of alg changes in DRPF file
	private const int MAX_DATA_CHANGES = 127;			// max number of data area changes in DRPF file

	// **** main API
	// 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
 
    public static IntPtr LoadDRPFFromFile(string drpf_file, out int err_code)
    {
        if (IntPtr.Size == 4)
			return LoadDRPFFromFile32.LoadDRPFFromFile(drpf_file, out err_code);
        return LoadDRPFFromFile64.LoadDRPFFromFile(drpf_file, out err_code);
    }

	// we have to do some marshalling of arrays of bytes into arrays of structures
	public static int ReadDRPFEx(string drpf_file, out REMOTEINFO RemoteInfo, out LICENCEINFO[] LicenceInfo, out ALGCHANGE[] AlgChanges, out DATACHANGE[] DataChanges, out int drpf_version, out int drpf_format)
	{
		int ret_code, i;
		byte[] licence_bytes = new byte[MAX_LICENCES * Marshal.SizeOf(typeof(LICENCEINFO))];
		byte[] licence_bytes_one_struct = new byte[Marshal.SizeOf(typeof(LICENCEINFO))];
		byte[] algchange_bytes = new byte[MAX_ALG_CHANGES * Marshal.SizeOf(typeof(ALGCHANGE))];
		byte[] algchange_bytes_one_struct = new byte[Marshal.SizeOf(typeof(ALGCHANGE))];
		byte[] datachange_bytes = new byte[MAX_DATA_CHANGES * Marshal.SizeOf(typeof(DATACHANGE))];
		byte[] datachange_bytes_one_struct = new byte[Marshal.SizeOf(typeof(DATACHANGE))];
		GCHandle MyGC;
		RemoteInfo = new REMOTEINFO();
		LicenceInfo = null;
		AlgChanges = null;
		DataChanges = null;

        if (IntPtr.Size == 4)
			ret_code = ReadDRPFEx32.ReadDRPFEx(drpf_file, out RemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, out drpf_version, out drpf_format);
		else 
			ret_code = ReadDRPFEx64.ReadDRPFEx(drpf_file, out RemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, out drpf_version, out drpf_format);
		
		if (ret_code != 0)
			return ret_code;
		
		// convert byte array to an array of structs
		LicenceInfo = new LICENCEINFO[RemoteInfo.NumLicences];
		for (i=0; i<RemoteInfo.NumLicences; i++)
		{
			Array.Copy(licence_bytes, i*Marshal.SizeOf(typeof(LICENCEINFO)), licence_bytes_one_struct, 0, Marshal.SizeOf(typeof(LICENCEINFO)));
			MyGC = GCHandle.Alloc(licence_bytes_one_struct, GCHandleType.Pinned);
			LicenceInfo[i] = (LICENCEINFO)Marshal.PtrToStructure(MyGC.AddrOfPinnedObject(), typeof(LICENCEINFO));
			MyGC.Free();
		}

		AlgChanges = new ALGCHANGE[RemoteInfo.NumAlgs];
		for (i=0; i<RemoteInfo.NumAlgs; i++)
		{
			Array.Copy(algchange_bytes, i*Marshal.SizeOf(typeof(ALGCHANGE)), algchange_bytes_one_struct, 0, Marshal.SizeOf(typeof(ALGCHANGE)));
			MyGC = GCHandle.Alloc(algchange_bytes_one_struct, GCHandleType.Pinned);
			AlgChanges[i] = (ALGCHANGE)Marshal.PtrToStructure(MyGC.AddrOfPinnedObject(), typeof(ALGCHANGE));
			MyGC.Free();
		}

		DataChanges = new DATACHANGE[RemoteInfo.NumDataChanges];
		for (i=0; i<RemoteInfo.NumDataChanges; i++)
		{
			Array.Copy(datachange_bytes, i*Marshal.SizeOf(typeof(DATACHANGE)), datachange_bytes_one_struct, 0, Marshal.SizeOf(typeof(DATACHANGE)));
			MyGC = GCHandle.Alloc(datachange_bytes_one_struct, GCHandleType.Pinned);
			DataChanges[i] = (DATACHANGE)Marshal.PtrToStructure(MyGC.AddrOfPinnedObject(), typeof(DATACHANGE));
			MyGC.Free();
		}

		return ret_code;
	}

	// we have to do some marshalling of an array of structures into an array of bytes
	public static IntPtr LoadDRPF(REMOTEINFO RemoteInfo, LICENCEINFO[] LicenceInfo, ALGCHANGE[] AlgChanges, DATACHANGE[] DataChanges, out int ret_code)
	{
		int i;
		byte[] licence_bytes = new byte[MAX_LICENCES * Marshal.SizeOf(typeof(LICENCEINFO))];
		byte[] licence_bytes_one_struct = new byte[Marshal.SizeOf(typeof(LICENCEINFO))];
		byte[] algchange_bytes = new byte[MAX_ALG_CHANGES * Marshal.SizeOf(typeof(ALGCHANGE))];
		byte[] algchange_bytes_one_struct = new byte[Marshal.SizeOf(typeof(ALGCHANGE))];
		byte[] datachange_bytes = new byte[MAX_DATA_CHANGES * Marshal.SizeOf(typeof(DATACHANGE))];
		byte[] datachange_bytes_one_struct = new byte[Marshal.SizeOf(typeof(DATACHANGE))];
		GCHandle MyGC;
		
		// convert array of structs to a byte array
		for (i=0; i<RemoteInfo.NumLicences; i++)
		{
			MyGC = GCHandle.Alloc(licence_bytes_one_struct, GCHandleType.Pinned);
			Marshal.StructureToPtr(LicenceInfo[i], MyGC.AddrOfPinnedObject(), false);
			MyGC.Free();
			Array.Copy(licence_bytes_one_struct, 0, licence_bytes, i*Marshal.SizeOf(typeof(LICENCEINFO)), Marshal.SizeOf(typeof(LICENCEINFO)));
		}

		for (i=0; i<RemoteInfo.NumAlgs; i++)
		{
			MyGC = GCHandle.Alloc(algchange_bytes_one_struct, GCHandleType.Pinned);
			Marshal.StructureToPtr(AlgChanges[i], MyGC.AddrOfPinnedObject(), false);
			MyGC.Free();
			Array.Copy(algchange_bytes_one_struct, 0, algchange_bytes, i*Marshal.SizeOf(typeof(ALGCHANGE)), Marshal.SizeOf(typeof(ALGCHANGE)));
		}

		for (i=0; i<RemoteInfo.NumDataChanges; i++)
		{
			MyGC = GCHandle.Alloc(datachange_bytes_one_struct, GCHandleType.Pinned);
			Marshal.StructureToPtr(DataChanges[i], MyGC.AddrOfPinnedObject(), false);
			MyGC.Free();
			Array.Copy(datachange_bytes_one_struct, 0, datachange_bytes, i*Marshal.SizeOf(typeof(DATACHANGE)), Marshal.SizeOf(typeof(DATACHANGE)));
		}

		if (IntPtr.Size == 4)
			return LoadDRPF32.LoadDRPF(ref RemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, out ret_code);
		return LoadDRPF64.LoadDRPF(ref RemoteInfo, licence_bytes, algchange_bytes, datachange_bytes, out ret_code);
	}

	// we have to do some marshalling of an array of structures into an array of bytes
	public static int WriteDRPFEx(IntPtr drpf_handle, string drpf_file, int drpf_format, int drpf_version, int flags)
	{
		if (IntPtr.Size == 4)
			return WriteDRPFEx32.WriteDRPFEx(drpf_handle, drpf_file, drpf_format, drpf_version, flags);
		return WriteDRPFEx64.WriteDRPFEx(drpf_handle, drpf_file, drpf_format, drpf_version, flags);
	}
	
	public static void UnloadDRPF(ref IntPtr drpf_handle)
	{
		if (IntPtr.Size == 4)
			UnloadDRPF32.UnloadDRPF(ref drpf_handle);
		else
			UnloadDRPF64.UnloadDRPF(ref drpf_handle);
		return;
	}

    public static int GenerateUpdateCode(IntPtr drpf_handle, uint dongle_number, int update_number, string ducf_file, string logfile, out UPDATE_CODE_OUTPUTS Outputs)
    {
 		if (IntPtr.Size == 4)
			return GenerateUpdateCode32.GenerateUpdateCode(drpf_handle, dongle_number, update_number, ducf_file, logfile, out Outputs);
		return GenerateUpdateCode64.GenerateUpdateCode(drpf_handle, dongle_number, update_number, ducf_file, logfile, out Outputs);
   }
    
	// do a bit of work here to make the function easier to use for C#
	public static string GetLastMessage()
	{
		int length, dummy;
		StringBuilder message;

		if (IntPtr.Size == 4)
		{
			GetLastMessage32.GetLastMessage(null, 0, out length);
			message = new StringBuilder(length);
			GetLastMessage32.GetLastMessage(message, length, out dummy);
		}
		else
		{
			GetLastMessage64.GetLastMessage(null, 0, out length);
			message = new StringBuilder(length);
			GetLastMessage64.GetLastMessage(message, length, out dummy);
		}

		return message.ToString();
	}

	public static int GetLastExtendedError()
	{
		if (IntPtr.Size == 4)
			return GetLastExtendedError32.GetLastExtendedError();
		return GetLastExtendedError64.GetLastExtendedError();
	}

}
