// Hasp.cpp: implementation of the CHasp class.
//
//////////////////////////////////////////////////////////////////////

#include "HaspCl.h"
#include <string.h>
#include <malloc.h>
#include <assert.h>


struct HaspPasswd CHasp::DemoPass[6] = { 
	{15417, 9632},  // DemoMA
	{29875, 28774}, // DemoMB
	{29813, 23912}, // DemoMC
	{5932, 25657},  // Demo3A
	{20580, 22012}, // Demo3B
	{10038, 15697}, // Demo3C
};


char CHasp::Descriptions[][12] = {
	"HASP4",
	"HASP4 M1",
	"HASP4 M4",
	"HASP4 Time",
	"HASP4 Net",
	"HASP3",
	"MemoHASP-1",
	"MemoHASP-4",
	"TimeHASP-1",
	"TimeHASP-4",
	"NetHASP"
};


//  NethaspProtocol inline implementation
//
CNethaspProtocol::CNethaspProtocol()
   : m_ConfigProtocol(0),
     m_MachineProtocol(0),
     m_SearchMethod(0)
{
}


CNethaspProtocol::CNethaspProtocol(int ConfigProtocol, int SearchMethod, int MachineProtocol)
   : m_ConfigProtocol(ConfigProtocol),
     m_MachineProtocol(MachineProtocol),
     m_SearchMethod(SearchMethod)
{
}


CHasp::CHasp()
{
	m_IsHasp = 0;
	m_Service = 0;
	m_Lpt = 0;
	m_Pass.p1 = 0;
	m_Pass.p2 = 0;
	m_SeedCode = 0;
	DemoPassCount = 6;
	pDemoPass = DemoPass;
	Init();
	SetParams(0, 0, 0, 0);
}


CHasp::CHasp(unsigned int Pass1, unsigned int Pass2, unsigned int LptNum, AutoSearchOptions Options, TryMode Mode)
{
	m_IsHasp = 0;
	m_Service = 0;
	m_Lpt = LptNum;
	m_Pass.p1 = Pass1;
	m_Pass.p2 = Pass2;
	m_SeedCode = 0;
	DemoPassCount = 6;
	pDemoPass = DemoPass;
	Init();
	SetParams(0, 0, 0, 0);
	AutoSearch(Options, Mode);
}


CHasp::~CHasp()
{
}


void CHasp::Init()
{
	m_HaspType = 0;
	m_IsHasp4 = 0;
	m_IsMemory = 0;
	m_IsTime = 0;
	m_IsRemote = 0;
	m_MemorySize = 0;
}


long CHasp::MakeLong(int hiword, int loword)
{
   return (((long)(unsigned short) hiword ) << 16) | ((unsigned short) loword);
}


void CHasp::SetParams(int par1, int par2, int par3, int par4)
{
	m_p1 = par1;
	m_p2 = par2;
	m_p3 = par3;
	m_p4 = par4;
}


void CHasp::GetParams(int* par1, int* par2, int* par3, int* par4) const
{
	if (par1)
		*par1 = m_p1;
	if (par2)
		*par2 = m_p2;
	if (par3)
		*par3 = m_p3;
	if (par4)
		*par4 = m_p4;
}


void CHasp::CallHasp()
{
	hasp(m_Service, m_SeedCode, m_Lpt, m_Pass.p1, m_Pass.p2, &m_p1, &m_p2, &m_p3, &m_p4);
}


void CHasp::CallHasp(unsigned int Service)
{
	m_Service = Service;
	CallHasp();
}


void CHasp::AutoSearch(AutoSearchOptions Options, TryMode Mode)
{
	switch (Mode)
	{
		case TryDemoMA:
			m_Pass = DemoPass[0];
			Refresh(Options);
			break;
		case TryDemoAll:
		{
			for (int i=0;i<DemoPassCount;i++)
			{
				try {
					m_Pass = pDemoPass[i];
					Refresh(Options);

					if (m_IsHasp)
					{
						m_IsDemo = i;
						break;
					}
				}
				catch (CHaspException e)
				{
					if (!(e.GetStatus() == HASPERR_TH_INVALID_PASSWORDS || e.GetStatus() == HASPERR_MH_INVALID_PASSWORDS))
						throw e;
				}
			}
			break;
		}
		case TryNothing:
		default:
			Refresh(Options);
	}
}



void CHasp::SetNetHaspProperties()
{
	m_IsRemote = TRUE;
	m_MemorySize = NETHASP_CAPACITY;
	m_IsMemory = TRUE;
	m_IsHasp = TRUE;
}


void CHasp::SetHaspProperties()
{
	m_IsHasp = TRUE;
	switch (m_p2)
	{
		case HASPTYPE_HASP3:
			break;

		case HASPTYPE_MEMOHASP:
			m_IsMemory = TRUE;
			m_MemorySize = (m_p1 == 1) ? MEMOHASP1_CAPACITY : MEMOHASP4_CAPACITY;
			break;

		case HASPTYPE_TIMEHASP:
			m_IsMemory = TRUE;
			m_IsTime = TRUE;
			m_MemorySize = MEMOHASP1_CAPACITY;
			break;

		case HASPTYPE_TIMEHASP4:
			m_IsMemory = TRUE;
			m_IsTime = TRUE;
			m_MemorySize = MEMOHASP4_CAPACITY;
			break;
		default:
			m_IsHasp = FALSE;
	}
}


void CHasp::CheckHasp4()
{
	m_IsHasp4 = TRUE;
	try
	{
		char block[8];
		EncodeByteBlock((void *) &block, 0, 8);
	}
	catch (CHaspException e)
	{
		m_IsHasp4 = 0;
		if (e.GetStatus() && e.GetStatus() != HASPERR_INVALID_SERVICE && e.GetStatus() != HASPERR_HARDWARE_NOT_SUPPORTED)
		{
			throw (e);
		}
	}
}


void CHasp::Refresh(AutoSearchOptions Options)
{
	Init();

	if (m_Pass.p1 == 0 && m_Pass.p2 == 0)
	{
		throw CHaspException(HASPERR_INVALID_PARAMETER);
	}

	if (Options == NetOnly && m_Lpt == 0)
		m_Lpt = 1;

	if (Options == NetFirst || Options == NetOnly)
	{
		try 
		{
			NetHASP_Login();
		}
		catch (CHaspException &e)
		{
			if (Options == NetOnly)
				throw e;
		}

		if (!m_p3)
		{
			SetNetHaspProperties();
			goto dotherest;
		}
	}

	CallHasp(LOCALHASP_ISHASP);
	if (!m_p1)
	{
		if (Options == LocalOnly)
		{
			throw CHaspException((m_p3 == 0) ? HASPERR_HASP_NOT_FOUND : m_p3, LOCALHASP_ISHASP);
		}
		else
		{
			NetHASP_Login();

			if (!m_p3)
			{
				SetNetHaspProperties();
				goto dotherest;
			}
		}
	}

	CallHasp(LOCALHASP_HASPSTATUS);

	if (m_p3 == 0)
		throw CHaspException((m_p3 == 0) ? HASPERR_HASP_NOT_FOUND : m_p3, LOCALHASP_HASPSTATUS);

	if (m_Lpt == 0)
		m_Lpt = m_p3;

	SetHaspProperties();

dotherest:
	if (m_IsHasp)
	{
		// Is Hasp4 
		CheckHasp4();
		SetDescription();
	}
	else
		throw CHaspException(HASPERR_HASP_NOT_FOUND);
}


char* CHasp::GetDescription()
{
	return m_Description;
}


void CHasp::SetDescription()
{
	if (m_IsHasp4)
	{
		if (m_IsRemote)
			strcpy(m_Description, Descriptions[4]);
		else if (m_IsTime)
			strcpy(m_Description, Descriptions[3]);
		else if(m_IsMemory)
			strcpy(m_Description, Descriptions[(m_MemorySize==MEMOHASP4_CAPACITY) ? 2:1]);
		else
			strcpy(m_Description, Descriptions[0]);
	}
	else
	{
		if (m_IsRemote)
			strcpy(m_Description, Descriptions[10]);
		else if (m_IsTime)
			strcpy(m_Description, Descriptions[(m_MemorySize==MEMOHASP4_CAPACITY) ? 9:8]);
		else if(m_IsMemory)
			strcpy(m_Description, Descriptions[(m_MemorySize==MEMOHASP4_CAPACITY) ? 7:6]);
		else
			strcpy(m_Description, Descriptions[5]);
	}
}


int CHasp::IsHasp(int CheckAgain) 
{
	if (!m_IsHasp || CheckAgain)
	{
		CallHasp(LOCALHASP_ISHASP);
		return (m_p1 != 0);
	}
	else
	{
		return m_IsHasp;
	}
}


int CHasp::IsSimpleHasp() const
{
	return !m_IsMemory;
}


int CHasp::IsMemoHasp() const
{
	return (m_IsMemory && !m_IsTime && !m_IsRemote);
}


int CHasp::IsTimeHasp() const
{
	return m_IsTime;
}


int CHasp::IsHasp4() const
{
	return m_IsHasp4;
}


int CHasp::IsRemote() const
{
	return m_IsRemote;
}


int CHasp::IsDemo() const
{
	return m_IsDemo;
}


void CHasp::BlockOperation(const void* Block, unsigned int Index, unsigned int Size)
{
	SetParams(Index, Size, (int) 0, (int) Block);
	CallHasp();
	if (m_p3)
		throw CHaspException(this);
}


void CHasp::BlockOperation(unsigned int Service, const void *Block, unsigned int Index, unsigned int Size)
{
	m_Service = Service;
	BlockOperation(Block, Index, Size);
}


long CHasp::HaspID()
{
	if (m_IsTime)
		m_Service = TIMEHASP_HASPID;
	else if (m_IsRemote)
		m_Service = NETHASP_HASPID;
	else
		m_Service = MEMOHASP_HASPID;
	CallHasp();
	if (m_p3)
		throw CHaspException(this);

	return MakeLong(m_p2, m_p1);
}


void CHasp::HaspCode(int SeedCode)
{
	SetSeedCode(SeedCode);
	HaspCode();
}


void CHasp::HaspCode()
{
	m_Service = (m_IsRemote) ? NETHASP_HASPCODE : LOCALHASP_HASPCODE;
	CallHasp();
}


void CHasp::EncodeByteBlock(void *block, int index, int size)
{
	m_Service = (m_IsRemote) ? NETHASP_ENCODEDATA : LOCALHASP_ENCODEDATA;
	BlockOperation(block, index, size);
}


void CHasp::DecodeByteBlock(void *block, int index, int size)
{
	m_Service = (m_IsRemote) ? NETHASP_DECODEDATA : LOCALHASP_DECODEDATA;
	BlockOperation(block, index, size);
}


void CHasp::WriteWord(unsigned int index, unsigned int value)
{
	//assert(index*2 > m_MemorySize);

	m_Service = (m_IsRemote) ? NETHASP_WRITEMEMO : MEMOHASP_WRITEMEMO;
	SetParams(index, value, 0, 0);
	CallHasp();
	if (m_p3)
		throw CHaspException(this);
}


int CHasp::ReadWord(unsigned int index)
{
	//assert(index*2 > m_MemorySize);

	m_Service = (m_IsRemote) ? NETHASP_READMEMO : MEMOHASP_READMEMO;
	SetParams(index, 0, 0, 0);
	CallHasp();
	if (m_p3)
		throw CHaspException(this);

	return m_p2;
}


void CHasp::WriteWordBlock(const void *block, unsigned int index, unsigned int size)
{
	m_Service = (m_IsRemote) ? NETHASP_WRITEBLOCK : MEMOHASP_WRITEBLOCK;
	BlockOperation(block, index, size);
}


void CHasp::ReadWordBlock(void *block, unsigned int index, unsigned int size)
{
	if (!m_IsMemory)
		return;

	m_Service = (m_IsRemote) ? NETHASP_READBLOCK : MEMOHASP_READBLOCK;
	BlockOperation(block, index, size);
}


void CHasp::WriteByteBlock(const void *block, unsigned int index, unsigned int size)
{
	unsigned char *tmpBlock = (unsigned char *)malloc(size+4);
	unsigned int tmpIndex = index;
	unsigned int tmpSize = size;

	if (index%2)
	{
		tmpIndex--;
		tmpSize++;
		int tmpValue = ReadWord(tmpIndex/2);
		memcpy(tmpBlock, ((char *)&tmpValue), 1);
	}
	if (tmpSize%2)
	{
		tmpSize++;
		int tmpValue = ReadWord((tmpIndex+tmpSize-1)/2);
		memcpy(tmpBlock+(tmpSize-1), ((char *)&tmpValue)+1, 1);
	}

	m_Service = (m_IsRemote) ? NETHASP_WRITEBLOCK : MEMOHASP_WRITEBLOCK;
	memcpy(tmpBlock+(index-tmpIndex), block, size);
	BlockOperation(tmpBlock, tmpIndex/2, tmpSize/2);
	free(tmpBlock);
}


void CHasp::ReadByteBlock(void *block, unsigned int index, unsigned int size)
{
	unsigned char *tmpBlock = (unsigned char *)malloc(size+4);
	unsigned int tmpIndex = index;
	unsigned int tmpSize = size;

	if (index%2)
	{
		tmpIndex--;
		tmpSize++;
	}
	if (tmpSize%2)
	{
		tmpSize++;
	}
	m_Service = (m_IsRemote) ? NETHASP_READBLOCK : MEMOHASP_READBLOCK;
	BlockOperation(tmpBlock, tmpIndex/2, tmpSize/2);
	memcpy(block, tmpBlock+(index-tmpIndex), size);
	free(tmpBlock);
}


void CHasp::WriteString(const char *string, unsigned int index)
{
	WriteByteBlock(string, index, strlen(string)+1);
}


void CHasp::TimeHASP_WriteByte(unsigned int index, unsigned char value)
{
	SetParams(index, value, 0, 0);
	CallHasp(TIMEHASP_WRITEMEMO);
	if (m_p3)
		throw CHaspException(this);
}


unsigned char CHasp::TimeHASP_ReadByte(unsigned int index)
{
	SetParams(index, 0, 0, 0);
	CallHasp(TIMEHASP_READMEMO);
	if (m_p3)
		throw CHaspException(this);
	return m_p2;
}


void CHasp::TimeHASP_WriteBlock(const void *block, unsigned int index, unsigned int size)
{
	BlockOperation(TIMEHASP_WRITEBLOCK, block, index, size);
}


void CHasp::TimeHASP_ReadBlock(void *block, unsigned int index, unsigned int size)
{
	BlockOperation(TIMEHASP_READBLOCK, block, index, size);
}


void CHasp::TimeHASP_SetTime(int Hour, int Minute, int Second)
{
	SetParams(Second, Minute, 0, Hour);
	CallHasp(TIMEHASP_SETTIME);
	if (m_p3)
		throw CHaspException(this);
}


void CHasp::TimeHASP_SetDate(int Year, int Month, int Day)
{
	SetParams(Day, Month, 0, Year);
	CallHasp(TIMEHASP_SETDATE);
	if (m_p3)
		throw CHaspException(this);
}


void CHasp::TimeHASP_GetTime(int *Hour, int *Minute, int *Second)
{
	CallHasp(TIMEHASP_GETTIME);
	if (m_p3)
		throw CHaspException(this);

	*Second = m_p1;
	*Minute = m_p2;
	*Hour = m_p4;
}


void CHasp::TimeHASP_GetDate(int *Year, int *Month, int *Day)
{
	CallHasp(TIMEHASP_GETDATE);
	if (m_p3)
		throw CHaspException(this);

	*Day = m_p1;
	*Month = m_p2;
	*Year = m_p4;
}



void CHasp::NetHASP_LastStatus()
{
	CallHasp(NETHASP_LASTSTATUS);

	if (m_p3)
		throw CHaspException(this);
}


void CHasp::NetHASP_Login()
{
	CallHasp(NETHASP_LOGIN);
	NetHASP_LastStatus();
}


void CHasp::NetHASP_Logout()
{
	CallHasp(NETHASP_LOGOUT);
	NetHASP_LastStatus();
}


void CHasp::NetHASP_IdleTime(int IdleMinutes)
{
	m_SeedCode = IdleMinutes;
	CallHasp(NETHASP_SETIDLETIME);
	NetHASP_LastStatus();
}


void CHasp::NetHASP_SetProtocol(const CNethaspProtocol& prot)
{
	SetParams(prot.GetConfigProtocol(), prot.GetSearchMethod(),0,0);
	CallHasp(NETHASP_SETPROTOCOL);
}


void CHasp::NetHASP_GetProtocol(CNethaspProtocol& prot)
{
	CallHasp(NETHASP_GETPROTOCOL);
	prot = CNethaspProtocol(m_p1, m_p2, m_p4);
}


void CHasp::NetHASP_SetDefaultConfig()
{
	CallHasp(NETHASP_DEFAULTCONFIG);
}


void CHasp::NetHASP_SetServerByName(const char DATAFAR_* server)
{
	BlockOperation(NETHASP_SET_SERVER_BY_NAME, server, 0, strlen(server));
}


void CHasp::NetHASP_SetConfigFilename(const char DATAFAR_* filename)
{
	BlockOperation(NETHASP_SET_CONFIG_FILENAME, filename, 0, strlen(filename));
}


void CHasp::NetHASP_QueryLicense(unsigned int ProgNum, int *LicActive, int *LicMax, int *KeyType, int *ActRemain)
{
	CallHasp(NETHASP_QUERY_LICENSE);
	if (LicActive)
		*LicActive = m_p1;

	if (LicMax)
		*LicMax = m_p2;

	if (KeyType)
		*KeyType = m_p3;

	if (ActRemain)
		*ActRemain = m_p4;

	NetHASP_LastStatus();
}
