with Interfaces;     use Interfaces;
with Interfaces.C;   use Interfaces.C;
with System;         use System;
with DRIS;           use DRIS;
with Ada.Text_IO;    use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;

-- !! We strongly recommend that you read the "readme.txt" file in the sample code folder.
-- !! It contains important instructions on how to use the sample code

procedure apitest is

   MY_SDSN : constant int32 := 	10101;
   -- !!!! change this value to be the value of your SDSN (demo = 10101)

   -- The sample code contains 10 functions. You will not need to use all these
   -- functions in your code. Just use which ever functions are most appropriate
   -- and customise in your own way. The sample code is just a guide. You should
   -- implement the protection in a stronger way using the techniques described in
   -- the "Increasing your Protection" chapter of the manual.
   --
   -- If you are using Dinkey Lite then you can only use 4 functions:
   -- ProtCheck, ProtCheckWithAlg, ProtCheckEnc, ProtCheckWithAlgEnc

   -- this file contains 10 protection-check functions.
   -- ProtCheck            a standard protection check
   -- ProtCheckWithAlg     a protection check & executing an Algorithm
   -- WriteBytes           a protection check & write data to the dongle
   -- ReadBytes            a protection check & read data from the dongle
   -- EncryptUserData      a protection check & encrypting data and then decrypting the data

   -- these functions are the same as the functions listed above but encrypting all parameters passed to our API
   -- ProtCheckEnc          a standard protection check
   -- ProtCheckWithAlgEnc   a protection check & executing an Algorithm
   -- WriteBytesEnc         a protection check & write data to the dongle
   -- ReadBytesEnc          a protection check & read data from the dongle
   -- EncryptUserDataEnc    a protection check & encrypting data and then decrypting the data

   -- useful routines that are called by our functions - implemented at the end of this file
   procedure random_set(dris : t_DRIS_access);
   procedure DisplayError(ret_code : int32; extended_error : int32);

   -- *************************  our 10 functions **********************************

   dris : t_DRIS_access := new t_DRIS;

   ----------------------------------
   -- ProtCheck
   ----------------------------------
   function ProtCheck return int32 is
      ret_code : int32;
   begin

      -- set the DRIS to have random values
      random_set(dris);

      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := PROTECTION_CHECK;	-- standard protection check
      dris.flags := 0;			-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- later in your code you can check other values in the DRIS...
      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

	-- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      Put_Line ("ProtCheck worked");
	  return 0;
   end;

   ----------------------------------
   -- ProtCheckWithAlg
   ----------------------------------
   -- !!!! You should replace this function with the one generated by the
   -- "generate source code" button in Algorithm tab for DinkeyAdd (if you have specified an algorithm)
   -- or from DinkeyLook if you are using a Dinkey Lite dongle.
   function MyAlgorithm(a,b,c,d,e,f,g,h : int32) return int32 is
   begin
      return (a + b + c + d + e + f + g + h);
   end;

   -- NB for this to work you must program at least "user algorithm" into the dongle
   -- NB We have used a very simple algorithm here (in the MyAlgorithm function). You must patch this with the
   -- sample code generated by DinkeyAdd for the algorithm that you are using.  For Lite models copy the sample
   -- code from DinkeyLook
   function ProtCheckWithAlg return int32 is
      ret_code : int32;
   begin
      -- set the DRIS to have random values
      random_set(dris);
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := EXECUTE_ALGORITHM;		-- standard protection check & execute algorithm
      dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.alg_number := 1;			-- execute algorithm 1 (you do not need to specify this field if you are only using Lite models).
      -- you should remove these entries if you are using Dinkey Lite so that algorithm arguments are random
      dris.var_a := 1;				-- sample values
      dris.var_b := 2;
      dris.var_c := 3;
      dris.var_d := 4;
      dris.var_e := 5;
      dris.var_f := 6;
      dris.var_g := 7;
      dris.var_h := 8;

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- ...later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      -- if you want you can check the algorithm answer - however if algorithm is from your code then best to just use the answer in your code
      -- !!!! Make sure you have replaced the MyAlgorithm routine with the algorithm you have specified in DinkeyAdd
      if (MyAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h) /= dris.alg_answer) then
         Put_Line ("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine");
         return -1;
      end if;

      Put_Line ("ProtCheckWithAlg worked");
	  return 0;
   end;

   ----------------------------------
   -- WriteBytes
   ----------------------------------
   -- This writes the Data "Hello, World" to the dongle data area at offset 7
   -- In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
   function WriteBytes return int32 is
      szDataToWrite : Interfaces.C.char_array := Interfaces.C.To_C ("Hello, World!", True);		-- append null so we write that too!
      ret_code : int32;
   begin

      -- set the DRIS to have random values
      random_set(dris);

      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := WRITE_DATA_AREA;	-- standard protection check and write data
      dris.flags := 0;			-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.rw_offset := 7;
      dris.rw_length := szDataToWrite'Length;
      dris.rw_data_ptr := szDataToWrite'Address;

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ...later in your code you can check other values in the DRIS...
      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
		 return -1;
      end if;

      Put_Line ("WriteBytes worked");
	  return 0;
   end;

   ----------------------------------
   -- ReadBytes
   ----------------------------------
   -- This reads 14 bytes of data from offset 7 in the dongle data area
   -- In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
   -- In order for the data to be displayed properly by this routine it will need to be text data
   function ReadBytes return int32 is
      szDataRead : Interfaces.C.char_array (1..14);
      ret_code : int32;
   begin
      -- set the DRIS to have random values
      random_set(dris);
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := READ_DATA_AREA;		-- standard protection check & read data
      dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.rw_offset := 7;
      dris.rw_length := szDataRead'Length;    -- it also should read the null
      dris.rw_data_ptr := szDataRead'Address;

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ...later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      Put_Line("ReadBytes worked! Dinkey Dongle Data area, offset 7 is: " & Interfaces.C.To_Ada(szDataRead));
	  return 0;
   end;

   ----------------------------------
   -- EncryptUserData
   ----------------------------------
   -- This function will do a protection check and ask the dongle to encrypt some data using encryption key 1
   -- It will then do another protection check & decrypt the data
   function EncryptUserData return int32 is
      szData : Interfaces.C.char_array := Interfaces.C.To_C ("Hello, World!");
      ret_code : int32;
   begin
      -- set the DRIS to have random values
      random_set(dris);
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := ENCRYPT_USER_DATA;		-- standard protection check & encrypt user data
      dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.data_crypt_key_num := 1;
      dris.rw_length := szData'Length;
      dris.rw_data_ptr := szData'Address;

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ... could add code to check other elements of the DRIS, e.g. ret_code, sdsn, ...

      -- Now decrypt the same data to get the original values
      random_set(dris);
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := DECRYPT_USER_DATA;		-- standard protection check & decrypt user data
      dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.data_crypt_key_num := 1;
      dris.rw_length := szData'Length;
      dris.rw_data_ptr := szData'Address;

      ret_code := DDProtCheck(dris, System.Null_Address);

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ...later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      Put_Line("EncryptUserData worked. Data decrypted is: " & Interfaces.C.To_Ada (szData));
	  return 0;
   end;



   -- !!!!!!!!!!!   all the functions listed below encrypt all parameters to our API  !!!!!!!!!!!!!!!!!!!!!
   -- In order for them to work properly you need to choose "Encrypt DRIS" and "Encrypt Data passed to API"
   -- in the Extra Security tab of DinkeyAdd. In this example, for Data Encryption I use the r/w algorithm
   -- but the code can be modified easily to use the 3 encryption parameters.

   ----------------------------------
   -- MyRWAlgorithm
   ----------------------------------
   -- !!!! please overwrite this function with the one generated by DinkeyAdd for R/W Algorithm
   -- NB The logical operators xor, or, and only work on unsigned integers so you may have to convert signed to unsigned
   function MyRWAlgorithm(a,b,c,d,e,f,g,h : int32) return uint32 is
      a2,b2,c2,d2,e2,f2 : uint32;
      for a2'Address use a'Address;	  
      for b2'Address use b'Address;	  
      for c2'Address use c'Address;	  
      for d2'Address use d'Address;	  
      for e2'Address use e'Address;	  
      for f2'Address use f'Address;	  
   begin
		return a2 xor b2 xor c2 xor d2 xor e2 xor f2;
   end;

   ----------------------------------
   -- CryptDRIS
   ----------------------------------
   -- !!!! please overwrite the 3 parameters with the parameters specfied in DinkeyAdd
   -- NB seed32, seed64 and bigseed64 are a clever way of initialising the bigseed array
   procedure CryptDRIS(dris : t_DRIS_access; seed1, seed2 : uint32) is
      i,j,k : uint32;

      type t_seed32 is array (1..2) of uint32;
      type t_seed64 is array (1..1) of uint64;
      seed32 : t_seed32;
      seed64 : t_seed64;
      for seed32'Address use seed64'Address;

      type t_uint64_array is array (1..32) of uint64;
      bigseed64 : t_uint64_array;

      type t_uint8_array is array (uint8(0)..255) of uint8;
      bigseed : t_uint8_array;
      for bigseed'Address use bigseed64'Address;

      S : t_uint8_array;
      temp, t : uint8;

      dris_size : uint32 := t_DRIS'Size/8;
      type t_array is array (0..dris_size-1) of Interfaces.Unsigned_8;
      dris_array : t_array;
      for dris_array'Address use dris.all'Address;

   begin

      seed32 := (1=> seed1, 2 => seed2);
      bigseed64 := (others => seed64(1));

      for i in S'Range loop
         S(i) := i;
      end loop;

      j := 0;
      for i in S'Range loop
         j := (j + uint32(S(i)) + uint32(bigseed(i)) + 123) mod 256;      -- !!!! parameter 1  (123)
         temp := S(i);
         S(i) := S(uint8(j));
         S(uint8(j)) := temp;
      end loop;

      i := 0;
      j := 0;
      k := 16;

      while k < dris_size loop
         i := (i + 1) mod 256;
         j := (j + uint32(S(uint8(i))) + 212) mod 256;                    -- !!!! parameter 2 (212)
         temp := S(uint8(i));
         S(uint8(i)) := S(uint8(j));
         S(uint8(j)) := temp;
         t := uint8 (uint32(S(uint8(i)) + S(uint8(j)) + 97) mod 256);     -- !!!! parameter 3 (97)
         dris_array(k) := dris_array(k) xor S(t);
         k := k + 1;
      end loop;
   end;


   ----------------------------------
   -- CryptApiData
   ----------------------------------
   -- !!!! if you specify parameters then you need to overwrite the parameters in this function.
   --      However, if you use the R/W algorithm then you can use this function as it is.
   -- NB seed32, seed64 and bigseed64 are a clever way of initialising the bigseed array
   procedure CryptApiData (data : System.Address;
                           length : int32;
                           seed1, seed2 : uint32;
                           alg_answer : uint32)
   is
      i,j : uint32;

      type t_seed32 is array (1..2) of uint32;
      type t_seed64 is array (1..1) of uint64;
      seed32 : t_seed32;
      seed64 : t_seed64;
      for seed32'Address use seed64'Address;


      type t_uint64_array is array (1..32) of uint64;
      bigseed64 : t_uint64_array;

      type t_uint8_array is array (uint8(0)..255) of uint8;
      bigseed : t_uint8_array;
      for bigseed'Address use bigseed64'Address;

      S : t_uint8_array;
      temp, t : uint8;

      type t_array is array (0..length-1) of Interfaces.Unsigned_8;
      data_array : t_array;
      for data_array'Address use data;

   begin

      seed32 := (1=> seed1, 2 => seed2);
      bigseed64 := (others => seed64(1));

      for i in S'Range loop
         S(i) := i;
      end loop;

      j := 0;
      for i in S'Range loop
         j := (j + uint32(S(i)) + uint32(bigseed(i)) + (uint32(alg_answer) and uint32(255))) mod 256;    -- parameter 1
         temp := S(i);
         S(i) := S(uint8(j));
         S(uint8(j)) := temp;
      end loop;

      i := 0;
      j := 0;
      for k in data_array'Range loop
         i := (i + 1) mod 256;
         j := (j + uint32(S(uint8(i))) + (Shift_Right(uint32(alg_answer),8) and 255)) mod 256;           -- parameter 2
         temp := S(uint8(i));
         S(uint8(i)) := S(uint8(j));
         S(uint8(j)) := temp;
         t := uint8 ((uint32(S(uint8(i))) + uint32(S(uint8(j))) + uint32(Shift_Right(uint32(alg_answer),16) and 255)) mod 256);   -- parameter 3
         data_array(k) := data_array(k) xor S(t);
      end loop;
   end;

   ----------------------------------
   -- ProtCheckEnc
   ----------------------------------
   -- We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.
   -- NB if you are using the debug module then do not call CryptDRIS as DRIS encryption is not supported. See notes in C readme.txt
   function ProtCheckEnc return int32 is
      ret_code : int32;
   begin

      -- set the DRIS to have random values
      random_set(dris);					-- NB this also fills seed1, seed2 with random values
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := PROTECTION_CHECK;			-- standard protection check
      dris.flags := 0;					-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      ret_code := DDProtCheck(dris, System.Null_Address);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);		-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      Put_Line ("ProtCheckEnc worked");
      return 0;
   end;

   ----------------------------------
   -- ProtCheckWithAlgEnc
   ----------------------------------
   -- NB for this to work you must program at least "user algorithm" into the dongle
   -- NB We have used a very simple algorithm here (in the MyAlgorithm function). You must patch this with the
   -- sample code generated by DinkeyAdd for the algorithm that you are using. For Lite models copy the sample code from DinkeyLook.
   -- We encrypt the DRIS passed to the API. Patch the CryptDRIS function in this file from source code generated by DinkeyAdd.
   function ProtCheckWithAlgEnc return int32 is
      ret_code : int32;
   begin
      -- set the DRIS to have random values
      random_set(dris);					-- NB this also fills seed1, seed2 with random values
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := EXECUTE_ALGORITHM;			-- standard protection check & execute algorithm
      dris.flags := 0;					-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.alg_number := 1;				-- execute algorithm 1 (you do not need to specify this field if you are only using Lite models).
      -- you should remove these entries if you are using Dinkey Lite so that algorithm arguments are random
      dris.var_a := 1;					-- sample values
      dris.var_b := 2;
      dris.var_c := 3;
      dris.var_d := 4;
      dris.var_e := 5;
      dris.var_f := 6;
      dris.var_g := 7;
      dris.var_h := 8;

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      ret_code := DDProtCheck(dris, System.Null_Address);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);		-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- ...later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      -- if you want you can check the algorithm answer - however if algorithm is from your code then best to just use the answer in your code
      -- !!!! Make sure you have replaced the MyAlgorithm routine with the algorithm you have specified in DinkeyAdd
      if (MyAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h) /= dris.alg_answer) then
         Put_Line ("Dinkey protection error!\nYou have not patched your algorithm in the MyAlgorithm routine");
         return -1;
      end if;

      Put_Line ("ProtCheckWithAlgEnc worked");
      return 0;
   end;


   ----------------------------------
   -- WriteBytesEnc
   ----------------------------------
   -- This writes the Data "Hello, World!" to the dongle data area at offset 7
   -- In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
   -- We encrypt the DRIS and Data passed to the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
   function WriteBytesEnc return int32 is
      szDataToWrite : Interfaces.C.char_array := Interfaces.C.To_C ("Hello, World!", True);		-- append null so we write that too!
      ret_code : int32;
      alg_ans : uint32;
   begin

      -- set the DRIS to have random values
      random_set(dris);					-- NB this also fills seed1, seed2, var_a...var_h with random values
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := WRITE_DATA_AREA;			-- standard protection check & write data
      dris.flags := 0;					-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.rw_offset := 7;
      dris.rw_length := szDataToWrite'Length;
      dris.rw_data_ptr := szDataToWrite'Address;

      -- calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
      alg_ans := MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

      -- encrypt data we want to write.
      CryptApiData(szDataToWrite'Address, szDataToWrite'Length, dris.seed1, dris.seed2, alg_ans);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      ret_code := DDProtCheck(dris, System.Null_Address);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);		-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ...later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      Put_Line ("WriteBytesEnc worked");
      return 0;
   end;

   ----------------------------------
   -- ReadBytesEnc
   ----------------------------------
   -- This reads 14 bytes of data from offset 7 in the dongle data area
   -- In order for this function to work you will need to have a data area in your dongle that is at least 21 bytes long.
   -- In order for the data to be displayed properly by this routine it will need to be text data
   -- We encrypt the DRIS and Data passed from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
   function ReadBytesEnc return int32 is
      szDataRead : Interfaces.C.char_array (1..14);
      ret_code : int32;
      alg_ans : uint32;
   begin
      -- set the DRIS to have random values
      random_set(dris);					-- NB this also fills seed1, seed2, var_a...var_h with random values
      -- then set the values we want to use
      dris.header := ('D','R','I','S');
      dris.size := dris.all'Size/8;
      dris.funct := READ_DATA_AREA;			-- standard protection check & read data
      dris.flags := 0;					-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
      dris.rw_offset := 7;
      dris.rw_length := szDataRead'Length;
      dris.rw_data_ptr := szDataRead'Address;

      -- calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
      alg_ans := MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      ret_code := DDProtCheck(dris, System.Null_Address);

      CryptDRIS(dris, dris.seed1, dris.seed2);		-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

      if (ret_code /= 0) then
         DisplayError(ret_code, dris.ext_err);		-- display messages for any errors that have occurred
         return ret_code;
      end if;

      -- ...later in your code you can check other values in the DRIS...

      if (dris.sdsn /= MY_SDSN) then
         Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
         return -1;
      end if;

      -- later on in your program you can check the return code again
      if (dris.ret_code /= 0) then
         Put_Line ("Dinkey Dongle protection error");
         return -1;
      end if;

      -- decrypt data that was read.
      CryptApiData(szDataRead'Address, szDataRead'Length, dris.seed1, dris.seed2, alg_ans);
	  
      Put_Line("It worked! Dinkey Dongle Data area, offset 7 is: " & Interfaces.C.To_Ada(szDataRead));
	  return 0;
   end;



   ----------------------------------
   -- EncryptUserDataEnc
   ----------------------------------
-- *************************  EncryptUserDataEnc  ******************************
-- This function will do a protection check and ask the dongle to encrypt some data using encryption key 1
-- It will then do another protection & decrypt the data
-- We encrypt the DRIS and Data passed to/from the API. Patch the CryptDRIS and CryptApiData functions in this file from source code generated by DinkeyAdd.
   function EncryptUserDataEnc return int32 is
      szData : Interfaces.C.char_array := Interfaces.C.To_C ("Hello, World!");
      ret_code : int32;
      alg_ans : uint32;
   begin
	-- set the DRIS to have random values
	random_set(dris);
	-- then set the values we want to use
	dris.header := ('D','R','I','S');
	dris.size := dris.all'Size/8;
	dris.funct := ENCRYPT_USER_DATA;		-- standard protection check & encrypt user data
	dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num := 1;
	dris.rw_length := szData'Length;
	dris.rw_data_ptr := szData'Address;

	-- calculate r/w algorithm answer - NB need to replace MyRWAlgorithm code with code for r/w algorithm
	alg_ans := MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);

	-- encrypt data we want to pass.
	CryptApiData(szData'Address, szData'Length, dris.seed1, dris.seed2, alg_ans);

	CryptDRIS(dris, dris.seed1, dris.seed2);	-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code := DDProtCheck(dris, System.Null_Address);

	CryptDRIS(dris, dris.seed1, dris.seed2);	-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	if (ret_code /= 0) then
		DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
		return ret_code;
	end if;

	-- ... could add code to check other elements of the DRIS, e.g. ret_code, sdsn, ...

	-- Now decrypt the same data to get the original values
	random_set(dris);
	-- then set the values we want to use
	dris.header := ('D','R','I','S');
	dris.size := dris.all'Size/8;
	dris.funct := DECRYPT_USER_DATA;		-- standard protection check & decrypt user data
	dris.flags := 0;				-- no extra flags, but you may want to specify some if you want to start a network user or decrement execs,...
	dris.data_crypt_key_num := 1;
	dris.rw_length := szData'Length;
	dris.rw_data_ptr := szData'Address;

	CryptDRIS(dris, dris.seed1, dris.seed2);	-- encrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	ret_code := DDProtCheck(dris, System.Null_Address);

	CryptDRIS(dris, dris.seed1, dris.seed2);	-- decrypt DRIS (!!!!you should separate from DDProtCheck for greater security)

	-- ...later in your code you can check other values in the DRIS...
	if (ret_code /= 0) then
		DisplayError(ret_code, dris.ext_err);	-- display messages for any errors that have occurred
		return ret_code;
	end if;

	if (dris.sdsn /= MY_SDSN) then
		Put_Line ("Incorrect SDSN! Please modify your source code so that MY_SDSN is set to be your SDSN.");
		return -1;
	end if;

	-- later on in your program you can check the return code again
	if (dris.ret_code /= 0) then
		Put_Line ("Dinkey Dongle protection error");
		return -1;
	end if;

	alg_ans := MyRWAlgorithm(dris.var_a, dris.var_b, dris.var_c, dris.var_d, dris.var_e, dris.var_f, dris.var_g, dris.var_h);
	-- decrypt data that was passed to us by the API
	CryptApiData(szData'Address, szData'Length, dris.seed1, dris.seed2, alg_ans);

	Put_Line("It worked. Data decrypted is: " & Interfaces.C.To_Ada(szData));
	return 0;
   end;

   -- *************  useful routines that are called from our functions  **********

   ----------------------------------
   -- random_set
   ----------------------------------
   -- fills the buffer with length bytes of random data
   -- NB there are better ways of doing this (for example with the Windows Crypto API calls)
   procedure random_set(dris : t_DRIS_access)
   is
      package p_rand is new Ada.Numerics.Discrete_Random (Interfaces.Unsigned_8);

      G : p_rand.Generator;
      size : Natural := t_DRIS'Size/8;
      type t_array is array (1..size) of Interfaces.Unsigned_8;
      T : t_array;
      for T'Address use dris.all'Address;
   begin
      p_rand.Reset (G);
      for i in T'Range loop
         T(i) := p_rand.Random (G);
      end loop;
   end;

   ----------------------------------
   -- DisplayError
   ----------------------------------
   -- displays messages for the most common errors. You will want to change this for your code
   procedure DisplayError(ret_code : int32; extended_error : int32)
   is
      use Ada.Text_IO;
   begin
      case ret_code is
	when 401 => Put_Line ("Error! No dongles detected!");
	when 403 => Put_Line ("Error! The dongle detected has a different type to the one specified in DinkeyAdd.");
	when 404 => Put_Line ("Error! The dongle detected has a different model to those specified in DinkeyAdd.");
	when 409 => Put_Line ("Error! The dongle detected has not been programmed by DinkeyAdd.");
	when 410 => Put_Line ("Error! The dongle detected has a different Product Code to the one specified in DinkeyAdd.");
	when 411 => Put_Line ("Error! The dongle detected does not contain the licence associated with this program.");
	when 413 => Put_Line ("Error! This program has not been protected by DinkeyAdd. For guidance please read the DinkeyAdd chapter of the Dinkey manual.");
	when 417 => Put_Line ("Error! One or more of the parameters set in the DRIS is incorrect. This could be caused if you are encrypting the DRIS in your code but did not specify DRIS encryption in DinkeyAdd - or vice versa.");
	when 423 => Put_Line ("Error! The number of network users has been exceeded.");
	when 435 => Put_Line ("Error! DinkeyServer has not been detected on the network.");
	when 922 => Put_Line ("Error! The Software Key has expired.");
        when others =>
            Put_Line ("An error occurred checking the dongle.");
            Put_Line ("Error:" & int32'Image(ret_code) & ", Extended Error:" & int32'Image(extended_error) );
      end case;
   end;

   ----------------------------------
   -- main
   ----------------------------------

begin
   -- call the function(s) of your choice here
   -- by default I have chosen a standard protection check

   if (ProtCheck /= 0) then
      return;	    							-- terminate your program
   end if;
   
   --  continue your program

end apitest;
