import random
import sys

#define constants

#these constants depend on the bitness of the OS - the DRIS is 4 bytes bigger for 64-bit code
if sys.maxsize > 2**32:
	DRIS_SIZE = 564         # DRIS size is bigger for 64-bit (it has an 8-byte pointer)
	python64 = 4            # so we can adjust structure offsets for 64-bit python
else:
	DRIS_SIZE = 560
	python64 = 0

#function values
PROTECTION_CHECK    = 1     # checks for dongle, check program params...
EXECUTE_ALGORITHM   = 2     # protection check + calculate answer for specified algorithm with specified inputs
WRITE_DATA_AREA     = 3     # protection check + writes dongle data area
READ_DATA_AREA      = 4     # protection check + reads dongle data area
ENCRYPT_USER_DATA   = 5     # protection check + the dongle will encrypt user data
DECRYPT_USER_DATA   = 6     # protection check + the dongle will decrypt user data
FAST_PRESENCE_CHECK	= 7     # checks for the presence of the correct dongle only with minimal security, no flags allowed.
STOP_NET_USER       = 8     # stops a network user (a protection check is NOT performed)

# flags - can specify as many as you like
DEC_ONE_EXEC            = 1     # decrement execs by 1
DEC_MANY_EXECS          = 2     # decrement execs by number specified in execs_decrement
START_NET_USER          = 4     # starts a network user
USE_FUNCTION_ARGUMENT   = 16    # use the extra argument in the function for pointers
CHECK_LOCAL_FIRST       = 32    # always look in local ports before looking in network ports
CHECK_NETWORK_FIRST     = 64    # always look on the network before looking in local ports
USE_ALT_LICENCE_NAME    = 128	# use name specified in alt_licence_name instead of the default one
DONT_SET_MAXDAYS_EXPIRY = 256   # if the max days expiry date has not been calculated then do not do it this time
MATCH_DONGLE_NUMBER     = 512   # restrict the search to match the dongle number specified in the DRIS
DONT_RETURN_FD_DRIVE    = 1024  # if an FD dongle has been detected then don't return the flash drive/mount name

# set 4 bytes in our DRIS byte array from an integer
def set4bytes(data, offset, value):
	data[offset] = value & 0xff
	data[offset+1] = (value >> 8) & 0xff
	data[offset+2] = (value >> 16) & 0xff
	data[offset+3] = (value >> 24) & 0xff
	return

# trunctates a value to 32-bit and converts to a signed integer
def make_signed32bit(value):
	value = value & 0xffffffff
	if (value > (2**31)):
		value = value - (2**32)
	return value	

# get 4 bytes in our DRIS byte array and convert to an unsigned integer
# this returns an unsigned value, which is useful for dongle number, features, etc... 
def get4bytes(data, offset):
	return (data[offset] + data[offset+1]*0x100 + data[offset+2]*0x10000 + data[offset+3]*0x1000000)

# this returns a signed value, which is useful for alg_answer, expiry day...
def get4bytes_signed(data, offset):
	value = data[offset] + data[offset+1]*0x100 + data[offset+2]*0x10000 + data[offset+3]*0x1000000
	if (value > (2**31)):
		value = value - (2**32)
	return value

# create the DRIS structure as a byte array, populate it with random values, initialise the header and size fields
def create():
	dris = bytearray(DRIS_SIZE)
	random.seed()
	for n in range(DRIS_SIZE):
		dris[n] = random.randint(0, 255)
	dris[0] = ord('D')
	dris[1] = ord('R')
	dris[2] = ord('I')
	dris[3] = ord('S')
	set4bytes(dris, 4, DRIS_SIZE)
	return dris

# these functions set various values in the DRIS
def set_function(dris, value):
	set4bytes(dris, 16, value)

def set_flags(dris, value):
	set4bytes(dris, 20, value)

def set_execs_decrement(dris, value):
	set4bytes(dris, 24, value)

def set_data_crypt_key_num(dris, value):
	set4bytes(dris, 28, value)

def set_rw_offset(dris, value):
	set4bytes(dris, 32, value)

def set_rw_length(dris, value):
	set4bytes(dris, 36, value)

def set_alt_licence_name(dris, licence_name):
	dris[44+python64:44+python64+len(licence_name)] = licence_name
	dris[44+python64+len(licence_name)] = 0					# null-terminate the string

def set_var_a(dris, value):
	set4bytes(dris, 300+python64, value)

def set_var_b(dris, value):
	set4bytes(dris, 304+python64, value)

def set_var_c(dris, value):
	set4bytes(dris, 308+python64, value)

def set_var_d(dris, value):
	set4bytes(dris, 312+python64, value)

def set_var_e(dris, value):
	set4bytes(dris, 316+python64, value)

def set_var_f(dris, value):
	set4bytes(dris, 320+python64, value)

def set_var_g(dris, value):
	set4bytes(dris, 324+python64, value)

def set_var_h(dris, value):
	set4bytes(dris, 328+python64, value)

def set_alg_number(dris, value):
	set4bytes(dris, 332+python64, value)

# these functions get values from the DRIS
def get_ret_code(dris):
	return get4bytes(dris, 336+python64)

def get_ext_err(dris):
	return get4bytes(dris, 340+python64)

def get_type(dris):
	return get4bytes(dris, 344+python64)

def get_model(dris):
	return get4bytes(dris, 348+python64)

def get_sdsn(dris):
	return get4bytes(dris, 352+python64)

def get_prodcode(dris):
	for n in range(356+python64,368+python64):
		if dris[n] == 0:
			break
	return dris[356+python64:n].decode("utf-8")

def get_dongle_number(dris):
	return get4bytes(dris, 368+python64)

def get_update_number(dris):
	return get4bytes(dris, 372+python64)

def get_data_area_size(dris):
	return get4bytes(dris, 376+python64)

def get_max_alg_num(dris):
	return get4bytes(dris, 380+python64)

def get_execs(dris):
	return get4bytes(dris, 384+python64)

def get_exp_day(dris):
	return get4bytes_signed(dris, 388+python64)

def get_exp_month(dris):
	return get4bytes_signed(dris, 392+python64)

def get_exp_year(dris):
	return get4bytes_signed(dris, 396+python64)

def get_features(dris):
	return get4bytes(dris, 400+python64)

def get_net_users(dris):
	return get4bytes(dris, 404+python64)

def get_alg_answer(dris):
	return get4bytes_signed(dris, 408+python64)

def get_fd_capacity(dris):
	return get4bytes(dris, 412+python64)

def get_fd_drive(dris):
	for n in range(416+python64,544+python64):
		if dris[n] == 0:
			break
	return dris[416+python64:n].decode("utf-8")

def get_swkey_type(dris):
    return get4bytes(dris, 544+python64)

def get_swkey_exp_day(dris):
    return get4bytes(dris, 548+python64)

def get_swkey_exp_month(dris):
    return get4bytes(dris, 552+python64)

def get_swkey_exp_year(dris):
    return get4bytes(dris, 556+python64)
	
# you may also want to retrieve var_a values that you already entered into the dris!
def get_var_a(dris):
	return get4bytes_signed(dris, 300+python64)

def get_var_b(dris):
	return get4bytes_signed(dris, 304+python64)

def get_var_c(dris):
	return get4bytes_signed(dris, 308+python64)

def get_var_d(dris):
	return get4bytes_signed(dris, 312+python64)

def get_var_e(dris):
	return get4bytes_signed(dris, 316+python64)

def get_var_f(dris):
	return get4bytes_signed(dris, 320+python64)

def get_var_g(dris):
	return get4bytes_signed(dris, 324+python64)

def get_var_h(dris):
	return get4bytes_signed(dris, 328+python64)

# function to display the most common error codes.
# You will want to change this with your own error messages
def DisplayError(ret_code, extended_error):
	if (ret_code == 401):
		print("Error! No dongles detected!")
	elif (ret_code == 403):
		print("Error! The dongle detected has a different type to the one specified in DinkeyAdd.")
	elif (ret_code == 404):
		print("Error! The dongle detected has a different model to those specified in DinkeyAdd.")
	elif (ret_code == 409):
		print("Error! The dongle detected has not been programmed by DinkeyAdd.")
	elif (ret_code == 410):
		print("Error! The dongle detected has a different Product Code to the one specified in DinkeyAdd.")
	elif (ret_code == 411):
		print("Error! The dongle detected does not contain the licence associated with this program.")
	elif (ret_code == 413):
		print("Error! This program has not been protected by DinkeyAdd. For guidance please read the DinkeyAdd chapter of the Dinkey manual.")
	elif (ret_code == 417):
		print("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.")
	elif (ret_code == 423):
		print("Error! The number of network users has been exceeded.")
	elif (ret_code == 435):
		print("Error! DinkeyServer has not been detected on the network.")
	elif (ret_code == 922):
		print("Error! The Software Key has expired.")
	else:
		print("An error occurred checking the dongle.\nError: " + str(ret_code) + ", Extended Error: " + str(extended_error))
	return
