#include <filesystem>
#include <StdInc.h>
#include "Utility/InitFunction.h"
#include "Functions/Global.h"
#include "MinHook.h"
#include <Utility/Hooking.Patterns.h>
#include <thread>
#include <iostream>
#include <Windowsx.h>
#include <Functions/Games/ES3X/BanapassEmu.h>
#include <iphlpapi.h>
#include <winternl.h>
#include <WinDNS.h>
#include <Functions/Games/ES3X/TouchEmu.h>
#ifdef _M_AMD64
#pragma optimize("", off)
#pragma comment(lib, "Ws2_32.lib")

extern LPCSTR hookPort;
static uintptr_t imageBase;
static unsigned char hasp_buffer[0xD40];
static const char *ipaddr;
HWND windowHandle = 0;

#define HASP_STATUS_OK 0
static unsigned int Hook_hasp_login(int feature_id, void *vendor_code, int hasp_handle)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_login\n");
#endif
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_logout(int hasp_handle)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_logout\n");
#endif
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_encrypt(int hasp_handle, unsigned char *buffer, unsigned int buffer_size)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_encrypt\n");
#endif
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_decrypt(int hasp_handle, unsigned char *buffer, unsigned int buffer_size)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_decrypt\n");
#endif
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_get_size(int hasp_handle, int hasp_fileid, unsigned int *hasp_size)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_get_size\n");
#endif
	*hasp_size = 0xD40; // Max addressable size by the game... absmax is 4k
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_read(int hasp_handle, int hasp_fileid, unsigned int offset, unsigned int length, unsigned char *buffer)
{
#ifdef _DEBUG
	OutputDebugStringA("hasp_read\n");
#endif
	memcpy(buffer, hasp_buffer + offset, length);
	return HASP_STATUS_OK;
}

static unsigned int Hook_hasp_write(int hasp_handle, int hasp_fileid, unsigned int offset, unsigned int length, unsigned char *buffer)
{
	return HASP_STATUS_OK;
}

typedef int(WINAPI *BIND)(SOCKET, CONST SOCKADDR *, INT);
static BIND pbind = NULL;

static unsigned int WINAPI Hook_bind(SOCKET s, const sockaddr *addr, int namelen)
{
	sockaddr_in bindAddr = {0};
	bindAddr.sin_family = AF_INET;
	bindAddr.sin_addr.s_addr = inet_addr("192.168.96.20");
	bindAddr.sin_port = htons(50765);
	if (addr == (sockaddr *)&bindAddr)
	{
		sockaddr_in bindAddr2 = {0};
		bindAddr2.sin_family = AF_INET;
		bindAddr2.sin_addr.s_addr = inet_addr(ipaddr);
		bindAddr2.sin_port = htons(50765);
		return pbind(s, (sockaddr *)&bindAddr2, namelen);
	}
	else
	{
		return pbind(s, addr, namelen);
	}
}

static BOOL FileExists(char *szPath)
{
	DWORD dwAttrib = GetFileAttributesA(szPath);

	return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
			!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

static int ReturnTrue()
{
	return 1;
}

static BYTE GenerateChecksum(unsigned char *myArray, int index, int length)
{
	BYTE crc = 0;
	for (int i = 0; i < length; i++)
	{
		crc += myArray[index + i];
	}
	return crc & 0xFF;
}

static void __fastcall GenerateDongleData(bool isTerminal)
{
	memset(hasp_buffer, 0, 0xD40);
	hasp_buffer[0] = 0x01;
	hasp_buffer[0x13] = 0x01;
	hasp_buffer[0x17] = 0x0A;
	hasp_buffer[0x1B] = 0x04;
	hasp_buffer[0x1C] = 0x3B;
	hasp_buffer[0x1D] = 0x6B;
	hasp_buffer[0x1E] = 0x40;
	hasp_buffer[0x1F] = 0x87;

	hasp_buffer[0x23] = 0x01;
	hasp_buffer[0x27] = 0x0A;
	hasp_buffer[0x2B] = 0x04;
	hasp_buffer[0x2C] = 0x3B;
	hasp_buffer[0x2D] = 0x6B;
	hasp_buffer[0x2E] = 0x40;
	hasp_buffer[0x2F] = 0x87;

	const char *sn_normal = "285013501138";
	const char *sn_terminal = "285011501138";
	const char *custom_sn = "CUSTOMSN1234"; // 12 chars
	char original_sn[13] = {0};

	if (isTerminal)
	{
		memcpy(hasp_buffer + 0xD00, sn_terminal, 12);
		// Save original S/N
		memcpy(original_sn, hasp_buffer + 0xD00, 12);
		// Overlap with custom S/N
		memcpy(hasp_buffer + 0xD00, custom_sn, 12);
		// Debug print
		OutputDebugStringA((std::string("[DEBUG] Dongle S/N (custom): ") + std::string((char *)(hasp_buffer + 0xD00), 12)).c_str());
		// Restore original S/N
		memcpy(hasp_buffer + 0xD00, original_sn, 12);
		hasp_buffer[0xD3E] = GenerateChecksum(hasp_buffer, 0xD00, 62);
		hasp_buffer[0xD3F] = hasp_buffer[0xD3E] ^ 0xFF;
	}
	else
	{
		memcpy(hasp_buffer + 0xD00, sn_normal, 12);
		// Save original S/N
		memcpy(original_sn, hasp_buffer + 0xD00, 12);
		// Overlap with custom S/N
		memcpy(hasp_buffer + 0xD00, custom_sn, 12);
		// Debug print
		OutputDebugStringA((std::string("[DEBUG] Dongle S/N (custom): ") + std::string((char *)(hasp_buffer + 0xD00), 12)).c_str());
		// Restore original S/N
		memcpy(hasp_buffer + 0xD00, original_sn, 12);
		hasp_buffer[0xD3E] = GenerateChecksum(hasp_buffer, 0xD00, 62);
		hasp_buffer[0xD3F] = hasp_buffer[0xD3E] ^ 0xFF;
	}
}

typedef void(WINAPI *OutputDebugStringA_t)(LPCSTR);

static void Hook_OutputDebugStringA(LPCSTR str)
{
	printf("debug> %s", str);
}

extern int *ffbOffset;
extern int *ffbOffset2;
extern int *ffbOffset3;
extern int *ffbOffset4;

static __int64(__fastcall *g_origMileageFix)(__int64);

static __int64 __fastcall MileageFix(__int64 a1)
{
	//*(DWORD*)(a1 + 224) = mileageValue;
	// auto result = g_origMileageFix(a1);
	// mileageValue += *(DWORD*)(a1 + 228);
	return g_origMileageFix(a1);
}

DNS_STATUS(WINAPI *g_origDnsQuery_A)(PCSTR pszName, WORD wType, DWORD Options, PVOID pExtra, PDNS_RECORD *ppQueryResults, PVOID *pReserved);

DNS_STATUS WINAPI DnsQuery_AHook(PCSTR pszName, WORD wType, DWORD Options, PVOID pExtra, PDNS_RECORD *ppQueryResults, PVOID *pReserved)
{
#if _DEBUG
	info("DnsQuery_AHook: %s", pszName);
#endif

	if (strncmp(pszName, "tenporouter.loc", 15) == 0 || strncmp(pszName, "bbrouter.loc", 15) == 0)
	{
		// TODO: make this configurable?
		const char *dnsName = config["Network"]["RouterIP"].c_str();

		return g_origDnsQuery_A(dnsName, wType, Options, pExtra, ppQueryResults, pReserved);
	}
	if (strncmp(pszName, "naominet.jp", 15) == 0)
	{
		const char *dnsName = "barnth.ddns.net";

		return g_origDnsQuery_A(dnsName, wType, Options, pExtra, ppQueryResults, pReserved);
	}
	if (strncmp(pszName, "mobirouter.loc", 14) == 0 || strncmp(pszName, "dslrouter.loc", 13) == 0)
	{
#if _DEBUG
		info("DnsQuery_AHooked: returning error.");
#endif
		return NULL;
	}

	return g_origDnsQuery_A(pszName, wType, Options, pExtra, ppQueryResults, pReserved);
}

DNS_STATUS(WINAPI *g_origDnsQueryEx)(PDNS_QUERY_REQUEST pQueryRequest, PDNS_QUERY_RESULT pQueryResults, PDNS_QUERY_CANCEL pCancelHandle);

DNS_STATUS WINAPI DnsQueryExHook(PDNS_QUERY_REQUEST pQueryRequest, PDNS_QUERY_RESULT pQueryResults, PDNS_QUERY_CANCEL pCancelHandle)
{
#if _DEBUG
	// info(true, "DnsQueryEx: %s", pQueryRequest->QueryName);
#endif
	if (wcsncmp(pQueryRequest->QueryName, L"mobirouter.loc", 14) == 0)
	{
		DNS_QUERY_REQUEST QueryRequest;
		QueryRequest.Version = pQueryRequest->Version;
		QueryRequest.QueryName = L"plsfail";
		QueryRequest.QueryOptions = pQueryRequest->QueryOptions;
		QueryRequest.pDnsServerList = pQueryRequest->pDnsServerList;
		QueryRequest.InterfaceIndex = pQueryRequest->InterfaceIndex;
		QueryRequest.pQueryCompletionCallback = pQueryRequest->pQueryCompletionCallback;
		QueryRequest.pQueryContext = pQueryRequest->pQueryContext;

		return g_origDnsQueryEx(&QueryRequest, pQueryResults, pCancelHandle);
	}

	if (wcsncmp(pQueryRequest->QueryName, L"tenporouter.loc", 15) == 0 || wcsncmp(pQueryRequest->QueryName, L"bbrouter.loc", 15) == 0)
	{
		// TODO: make it configurable? Add check for other common sega stuff?
		wchar_t *wString = new wchar_t[4096];
		MultiByteToWideChar(CP_ACP, 0, config["Network"]["RouterIP"].c_str(), -1, wString, 4096);
		DNS_QUERY_REQUEST QueryRequest;
		QueryRequest.Version = pQueryRequest->Version;
		QueryRequest.QueryName = wString;
		QueryRequest.QueryOptions = pQueryRequest->QueryOptions;
		QueryRequest.pDnsServerList = pQueryRequest->pDnsServerList;
		QueryRequest.InterfaceIndex = pQueryRequest->InterfaceIndex;
		QueryRequest.pQueryCompletionCallback = pQueryRequest->pQueryCompletionCallback;
		QueryRequest.pQueryContext = pQueryRequest->pQueryContext;

		return g_origDnsQueryEx(&QueryRequest, pQueryResults, pCancelHandle);
	}

	return g_origDnsQueryEx(pQueryRequest, pQueryResults, pCancelHandle);
}

int(WSAAPI *g_origInetPtonW)(INT Family, PCWSTR pszAddrString, PVOID pAddrBuf);

int WSAAPI InetPtonWHook(INT Family, PCWSTR pszAddrString, PVOID pAddrBuf)
{
#if _DEBUG
	// infoW(true, L"InetPtonWHook: %s", pszAddrString);
#endif
	// idk yet if this is also needed or not.
	// if (wcsncmp(pszAddrString, L"tenporouter.loc", 15) == 0)
	//{
	//	return g_origInetPtonW(Family, L"localhost", pAddrBuf);
	//}
	// if (wcsncmp(pszAddrString, L"mobirouter.loc", 15) == 0)
	//{
	//	return g_origInetPtonW(Family, L"plsfail", pAddrBuf);
	//}

	return g_origInetPtonW(Family, pszAddrString, pAddrBuf);
}

int(WSAAPI *g_origgetaddrinfo)(PCSTR pNodeName, PCSTR pServiceName, const ADDRINFOA *pHints, PADDRINFOA *ppResult);

int WSAAPI getaddrinfoHook(PCSTR pNodeName, PCSTR pServiceName, const ADDRINFOA *pHints, PADDRINFOA *ppResult)
{
#if _DEBUG
	info("getaddrinfo: %s, %s", pNodeName, pServiceName);
#endif

	return g_origgetaddrinfo(pNodeName, pServiceName, pHints, ppResult);
}

// array size is 1107
static const unsigned char terminal_cert_v388[] = {
	0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49,
	0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x44,
	0x42, 0x44, 0x43, 0x43, 0x41, 0x6d, 0x32, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55,
	0x47, 0x73, 0x75, 0x41, 0x30, 0x53, 0x50, 0x37, 0x66, 0x63, 0x45, 0x73, 0x53, 0x31, 0x4e, 0x5a,
	0x63, 0x46, 0x70, 0x4f, 0x6b, 0x30, 0x59, 0x72, 0x50, 0x7a, 0x67, 0x77, 0x44, 0x51, 0x59, 0x4a,
	0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x46, 0x0a, 0x42, 0x51, 0x41,
	0x77, 0x67, 0x5a, 0x49, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59,
	0x54, 0x41, 0x6b, 0x70, 0x51, 0x4d, 0x51, 0x34, 0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51,
	0x49, 0x44, 0x41, 0x56, 0x55, 0x62, 0x32, 0x74, 0x35, 0x62, 0x7a, 0x45, 0x67, 0x4d, 0x42, 0x34,
	0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77, 0x58, 0x54, 0x6b, 0x46, 0x4e, 0x0a, 0x51, 0x30,
	0x38, 0x67, 0x51, 0x6b, 0x46, 0x4f, 0x52, 0x45, 0x46, 0x4a, 0x49, 0x45, 0x64, 0x68, 0x62, 0x57,
	0x56, 0x7a, 0x49, 0x45, 0x6c, 0x75, 0x59, 0x79, 0x34, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x42, 0x67,
	0x4e, 0x56, 0x42, 0x41, 0x73, 0x4d, 0x47, 0x55, 0x35, 0x6c, 0x64, 0x48, 0x64, 0x76, 0x63, 0x6d,
	0x73, 0x67, 0x55, 0x32, 0x56, 0x79, 0x64, 0x6d, 0x56, 0x79, 0x49, 0x45, 0x52, 0x6c, 0x0a, 0x63,
	0x47, 0x46, 0x79, 0x64, 0x47, 0x31, 0x6c, 0x62, 0x6e, 0x51, 0x78, 0x4c, 0x54, 0x41, 0x72, 0x42,
	0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x4a, 0x45, 0x35, 0x42, 0x54, 0x55, 0x4e, 0x50, 0x49,
	0x45, 0x4a, 0x42, 0x54, 0x6b, 0x52, 0x42, 0x53, 0x53, 0x42, 0x48, 0x59, 0x57, 0x31, 0x6c, 0x63,
	0x79, 0x42, 0x4a, 0x62, 0x6d, 0x4d, 0x67, 0x4c, 0x53, 0x42, 0x4f, 0x55, 0x30, 0x51, 0x67, 0x0a,
	0x51, 0x30, 0x45, 0x67, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x44, 0x41, 0x67, 0x46, 0x77, 0x30, 0x79,
	0x4d, 0x54, 0x41, 0x33, 0x4d, 0x54, 0x67, 0x78, 0x4f, 0x44, 0x55, 0x35, 0x4d, 0x54, 0x42, 0x61,
	0x47, 0x41, 0x38, 0x7a, 0x4d, 0x44, 0x41, 0x78, 0x4d, 0x44, 0x6b, 0x78, 0x4f, 0x54, 0x45, 0x34,
	0x4e, 0x54, 0x6b, 0x78, 0x4d, 0x46, 0x6f, 0x77, 0x67, 0x5a, 0x49, 0x78, 0x43, 0x7a, 0x41, 0x4a,
	0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6b, 0x70, 0x51, 0x4d, 0x51, 0x34,
	0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, 0x56, 0x55, 0x62, 0x32, 0x74,
	0x35, 0x62, 0x7a, 0x45, 0x67, 0x4d, 0x42, 0x34, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77,
	0x58, 0x54, 0x6b, 0x46, 0x4e, 0x51, 0x30, 0x38, 0x67, 0x51, 0x6b, 0x46, 0x4f, 0x52, 0x45, 0x46,
	0x4a, 0x0a, 0x49, 0x45, 0x64, 0x68, 0x62, 0x57, 0x56, 0x7a, 0x49, 0x45, 0x6c, 0x75, 0x59, 0x79,
	0x34, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x73, 0x4d, 0x47, 0x55,
	0x35, 0x6c, 0x64, 0x48, 0x64, 0x76, 0x63, 0x6d, 0x73, 0x67, 0x55, 0x32, 0x56, 0x79, 0x64, 0x6d,
	0x56, 0x79, 0x49, 0x45, 0x52, 0x6c, 0x63, 0x47, 0x46, 0x79, 0x64, 0x47, 0x31, 0x6c, 0x62, 0x6e,
	0x51, 0x78, 0x0a, 0x4c, 0x54, 0x41, 0x72, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x4a,
	0x45, 0x35, 0x42, 0x54, 0x55, 0x4e, 0x50, 0x49, 0x45, 0x4a, 0x42, 0x54, 0x6b, 0x52, 0x42, 0x53,
	0x53, 0x42, 0x48, 0x59, 0x57, 0x31, 0x6c, 0x63, 0x79, 0x42, 0x4a, 0x62, 0x6d, 0x4d, 0x67, 0x4c,
	0x53, 0x42, 0x4f, 0x55, 0x30, 0x51, 0x67, 0x51, 0x30, 0x45, 0x67, 0x55, 0x6d, 0x39, 0x76, 0x64,
	0x44, 0x43, 0x42, 0x0a, 0x6e, 0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
	0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x4f, 0x42, 0x6a, 0x51, 0x41, 0x77,
	0x67, 0x59, 0x6b, 0x43, 0x67, 0x59, 0x45, 0x41, 0x33, 0x47, 0x54, 0x4f, 0x41, 0x2f, 0x4a, 0x31,
	0x62, 0x69, 0x41, 0x6e, 0x71, 0x6c, 0x79, 0x70, 0x33, 0x36, 0x43, 0x55, 0x77, 0x55, 0x35, 0x42,
	0x63, 0x64, 0x63, 0x71, 0x0a, 0x39, 0x37, 0x4d, 0x61, 0x54, 0x77, 0x37, 0x68, 0x4d, 0x55, 0x66,
	0x54, 0x71, 0x58, 0x57, 0x33, 0x32, 0x35, 0x33, 0x69, 0x71, 0x48, 0x4b, 0x33, 0x50, 0x72, 0x68,
	0x58, 0x78, 0x45, 0x78, 0x7a, 0x39, 0x4a, 0x37, 0x6e, 0x70, 0x79, 0x2b, 0x31, 0x34, 0x36, 0x76,
	0x31, 0x75, 0x38, 0x4c, 0x33, 0x4e, 0x70, 0x66, 0x41, 0x48, 0x44, 0x58, 0x37, 0x45, 0x57, 0x6c,
	0x4c, 0x48, 0x51, 0x58, 0x57, 0x0a, 0x6b, 0x4e, 0x6f, 0x49, 0x37, 0x4a, 0x6b, 0x56, 0x56, 0x59,
	0x73, 0x32, 0x35, 0x66, 0x71, 0x58, 0x4c, 0x41, 0x4b, 0x79, 0x49, 0x4a, 0x4a, 0x4f, 0x42, 0x48,
	0x52, 0x43, 0x66, 0x46, 0x4f, 0x38, 0x50, 0x46, 0x79, 0x35, 0x32, 0x51, 0x53, 0x4c, 0x5a, 0x53,
	0x70, 0x7a, 0x55, 0x63, 0x68, 0x58, 0x4b, 0x61, 0x75, 0x73, 0x79, 0x79, 0x45, 0x6b, 0x32, 0x44,
	0x78, 0x70, 0x2f, 0x78, 0x61, 0x69, 0x0a, 0x65, 0x49, 0x64, 0x69, 0x68, 0x50, 0x76, 0x37, 0x49,
	0x55, 0x50, 0x6e, 0x70, 0x64, 0x30, 0x43, 0x41, 0x77, 0x45, 0x41, 0x41, 0x61, 0x4e, 0x54, 0x4d,
	0x46, 0x45, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46,
	0x45, 0x77, 0x37, 0x67, 0x4b, 0x32, 0x4c, 0x34, 0x48, 0x2f, 0x79, 0x31, 0x52, 0x4a, 0x67, 0x35,
	0x32, 0x64, 0x33, 0x66, 0x64, 0x70, 0x62, 0x0a, 0x78, 0x6c, 0x71, 0x4a, 0x4d, 0x42, 0x38, 0x47,
	0x41, 0x31, 0x55, 0x64, 0x49, 0x77, 0x51, 0x59, 0x4d, 0x42, 0x61, 0x41, 0x46, 0x45, 0x77, 0x37,
	0x67, 0x4b, 0x32, 0x4c, 0x34, 0x48, 0x2f, 0x79, 0x31, 0x52, 0x4a, 0x67, 0x35, 0x32, 0x64, 0x33,
	0x66, 0x64, 0x70, 0x62, 0x78, 0x6c, 0x71, 0x4a, 0x4d, 0x41, 0x38, 0x47, 0x41, 0x31, 0x55, 0x64,
	0x45, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x0a, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x41,
	0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45,
	0x46, 0x42, 0x51, 0x41, 0x44, 0x67, 0x59, 0x45, 0x41, 0x58, 0x46, 0x4c, 0x73, 0x70, 0x52, 0x47,
	0x67, 0x2b, 0x53, 0x4b, 0x73, 0x6f, 0x44, 0x33, 0x72, 0x48, 0x4c, 0x68, 0x47, 0x32, 0x31, 0x2f,
	0x74, 0x31, 0x64, 0x64, 0x6b, 0x6b, 0x4e, 0x70, 0x54, 0x0a, 0x78, 0x56, 0x4d, 0x31, 0x66, 0x4b,
	0x61, 0x33, 0x6a, 0x45, 0x45, 0x35, 0x7a, 0x62, 0x6d, 0x6d, 0x4a, 0x74, 0x67, 0x4e, 0x51, 0x47,
	0x33, 0x68, 0x59, 0x41, 0x77, 0x69, 0x2f, 0x71, 0x6e, 0x77, 0x57, 0x72, 0x34, 0x39, 0x55, 0x6d,
	0x41, 0x57, 0x49, 0x68, 0x5a, 0x7a, 0x43, 0x43, 0x44, 0x51, 0x53, 0x2f, 0x62, 0x30, 0x65, 0x31,
	0x32, 0x6c, 0x5a, 0x2b, 0x6e, 0x65, 0x33, 0x32, 0x77, 0x4b, 0x0a, 0x4e, 0x45, 0x2b, 0x73, 0x35,
	0x6c, 0x31, 0x53, 0x71, 0x52, 0x41, 0x51, 0x46, 0x36, 0x4e, 0x68, 0x75, 0x4f, 0x35, 0x4d, 0x37,
	0x6d, 0x51, 0x32, 0x4a, 0x47, 0x38, 0x69, 0x6e, 0x48, 0x6e, 0x4e, 0x34, 0x59, 0x77, 0x70, 0x72,
	0x66, 0x74, 0x45, 0x6f, 0x67, 0x76, 0x52, 0x2f, 0x69, 0x7a, 0x65, 0x61, 0x6b, 0x35, 0x4d, 0x4d,
	0x7a, 0x43, 0x77, 0x36, 0x34, 0x44, 0x66, 0x44, 0x43, 0x79, 0x41, 0x0a, 0x6d, 0x72, 0x4a, 0x6f,
	0x71, 0x77, 0x75, 0x71, 0x69, 0x74, 0x34, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e,
	0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d,
	0x2d, 0x2d, 0x0a};

// array size is 1107
static const unsigned char v388_ca_cert[] = {
	0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49,
	0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x44,
	0x42, 0x44, 0x43, 0x43, 0x41, 0x6d, 0x32, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x55,
	0x47, 0x73, 0x75, 0x41, 0x30, 0x53, 0x50, 0x37, 0x66, 0x63, 0x45, 0x73, 0x53, 0x31, 0x4e, 0x5a,
	0x63, 0x46, 0x70, 0x4f, 0x6b, 0x30, 0x59, 0x72, 0x50, 0x7a, 0x67, 0x77, 0x44, 0x51, 0x59, 0x4a,
	0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x46, 0x0a, 0x42, 0x51, 0x41,
	0x77, 0x67, 0x5a, 0x49, 0x78, 0x43, 0x7a, 0x41, 0x4a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59,
	0x54, 0x41, 0x6b, 0x70, 0x51, 0x4d, 0x51, 0x34, 0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51,
	0x49, 0x44, 0x41, 0x56, 0x55, 0x62, 0x32, 0x74, 0x35, 0x62, 0x7a, 0x45, 0x67, 0x4d, 0x42, 0x34,
	0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77, 0x58, 0x54, 0x6b, 0x46, 0x4e, 0x0a, 0x51, 0x30,
	0x38, 0x67, 0x51, 0x6b, 0x46, 0x4f, 0x52, 0x45, 0x46, 0x4a, 0x49, 0x45, 0x64, 0x68, 0x62, 0x57,
	0x56, 0x7a, 0x49, 0x45, 0x6c, 0x75, 0x59, 0x79, 0x34, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x42, 0x67,
	0x4e, 0x56, 0x42, 0x41, 0x73, 0x4d, 0x47, 0x55, 0x35, 0x6c, 0x64, 0x48, 0x64, 0x76, 0x63, 0x6d,
	0x73, 0x67, 0x55, 0x32, 0x56, 0x79, 0x64, 0x6d, 0x56, 0x79, 0x49, 0x45, 0x52, 0x6c, 0x0a, 0x63,
	0x47, 0x46, 0x79, 0x64, 0x47, 0x31, 0x6c, 0x62, 0x6e, 0x51, 0x78, 0x4c, 0x54, 0x41, 0x72, 0x42,
	0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x4a, 0x45, 0x35, 0x42, 0x54, 0x55, 0x4e, 0x50, 0x49,
	0x45, 0x4a, 0x42, 0x54, 0x6b, 0x52, 0x42, 0x53, 0x53, 0x42, 0x48, 0x59, 0x57, 0x31, 0x6c, 0x63,
	0x79, 0x42, 0x4a, 0x62, 0x6d, 0x4d, 0x67, 0x4c, 0x53, 0x42, 0x4f, 0x55, 0x30, 0x51, 0x67, 0x0a,
	0x51, 0x30, 0x45, 0x67, 0x55, 0x6d, 0x39, 0x76, 0x64, 0x44, 0x41, 0x67, 0x46, 0x77, 0x30, 0x79,
	0x4d, 0x54, 0x41, 0x33, 0x4d, 0x54, 0x67, 0x78, 0x4f, 0x44, 0x55, 0x35, 0x4d, 0x54, 0x42, 0x61,
	0x47, 0x41, 0x38, 0x7a, 0x4d, 0x44, 0x41, 0x78, 0x4d, 0x44, 0x6b, 0x78, 0x4f, 0x54, 0x45, 0x34,
	0x4e, 0x54, 0x6b, 0x78, 0x4d, 0x46, 0x6f, 0x77, 0x67, 0x5a, 0x49, 0x78, 0x43, 0x7a, 0x41, 0x4a,
	0x0a, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x59, 0x54, 0x41, 0x6b, 0x70, 0x51, 0x4d, 0x51, 0x34,
	0x77, 0x44, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x49, 0x44, 0x41, 0x56, 0x55, 0x62, 0x32, 0x74,
	0x35, 0x62, 0x7a, 0x45, 0x67, 0x4d, 0x42, 0x34, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77,
	0x58, 0x54, 0x6b, 0x46, 0x4e, 0x51, 0x30, 0x38, 0x67, 0x51, 0x6b, 0x46, 0x4f, 0x52, 0x45, 0x46,
	0x4a, 0x0a, 0x49, 0x45, 0x64, 0x68, 0x62, 0x57, 0x56, 0x7a, 0x49, 0x45, 0x6c, 0x75, 0x59, 0x79,
	0x34, 0x78, 0x49, 0x6a, 0x41, 0x67, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x73, 0x4d, 0x47, 0x55,
	0x35, 0x6c, 0x64, 0x48, 0x64, 0x76, 0x63, 0x6d, 0x73, 0x67, 0x55, 0x32, 0x56, 0x79, 0x64, 0x6d,
	0x56, 0x79, 0x49, 0x45, 0x52, 0x6c, 0x63, 0x47, 0x46, 0x79, 0x64, 0x47, 0x31, 0x6c, 0x62, 0x6e,
	0x51, 0x78, 0x0a, 0x4c, 0x54, 0x41, 0x72, 0x42, 0x67, 0x4e, 0x56, 0x42, 0x41, 0x4d, 0x4d, 0x4a,
	0x45, 0x35, 0x42, 0x54, 0x55, 0x4e, 0x50, 0x49, 0x45, 0x4a, 0x42, 0x54, 0x6b, 0x52, 0x42, 0x53,
	0x53, 0x42, 0x48, 0x59, 0x57, 0x31, 0x6c, 0x63, 0x79, 0x42, 0x4a, 0x62, 0x6d, 0x4d, 0x67, 0x4c,
	0x53, 0x42, 0x4f, 0x55, 0x30, 0x51, 0x67, 0x51, 0x30, 0x45, 0x67, 0x55, 0x6d, 0x39, 0x76, 0x64,
	0x44, 0x43, 0x42, 0x0a, 0x6e, 0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
	0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x4f, 0x42, 0x6a, 0x51, 0x41, 0x77,
	0x67, 0x59, 0x6b, 0x43, 0x67, 0x59, 0x45, 0x41, 0x33, 0x47, 0x54, 0x4f, 0x41, 0x2f, 0x4a, 0x31,
	0x62, 0x69, 0x41, 0x6e, 0x71, 0x6c, 0x79, 0x70, 0x33, 0x36, 0x43, 0x55, 0x77, 0x55, 0x35, 0x42,
	0x63, 0x64, 0x63, 0x71, 0x0a, 0x39, 0x37, 0x4d, 0x61, 0x54, 0x77, 0x37, 0x68, 0x4d, 0x55, 0x66,
	0x54, 0x71, 0x58, 0x57, 0x33, 0x32, 0x35, 0x33, 0x69, 0x71, 0x48, 0x4b, 0x33, 0x50, 0x72, 0x68,
	0x58, 0x78, 0x45, 0x78, 0x7a, 0x39, 0x4a, 0x37, 0x6e, 0x70, 0x79, 0x2b, 0x31, 0x34, 0x36, 0x76,
	0x31, 0x75, 0x38, 0x4c, 0x33, 0x4e, 0x70, 0x66, 0x41, 0x48, 0x44, 0x58, 0x37, 0x45, 0x57, 0x6c,
	0x4c, 0x48, 0x51, 0x58, 0x57, 0x0a, 0x6b, 0x4e, 0x6f, 0x49, 0x37, 0x4a, 0x6b, 0x56, 0x56, 0x59,
	0x73, 0x32, 0x35, 0x66, 0x71, 0x58, 0x4c, 0x41, 0x4b, 0x79, 0x49, 0x4a, 0x4a, 0x4f, 0x42, 0x48,
	0x52, 0x43, 0x66, 0x46, 0x4f, 0x38, 0x50, 0x46, 0x79, 0x35, 0x32, 0x51, 0x53, 0x4c, 0x5a, 0x53,
	0x70, 0x7a, 0x55, 0x63, 0x68, 0x58, 0x4b, 0x61, 0x75, 0x73, 0x79, 0x79, 0x45, 0x6b, 0x32, 0x44,
	0x78, 0x70, 0x2f, 0x78, 0x61, 0x69, 0x0a, 0x65, 0x49, 0x64, 0x69, 0x68, 0x50, 0x76, 0x37, 0x49,
	0x55, 0x50, 0x6e, 0x70, 0x64, 0x30, 0x43, 0x41, 0x77, 0x45, 0x41, 0x41, 0x61, 0x4e, 0x54, 0x4d,
	0x46, 0x45, 0x77, 0x48, 0x51, 0x59, 0x44, 0x56, 0x52, 0x30, 0x4f, 0x42, 0x42, 0x59, 0x45, 0x46,
	0x45, 0x77, 0x37, 0x67, 0x4b, 0x32, 0x4c, 0x34, 0x48, 0x2f, 0x79, 0x31, 0x52, 0x4a, 0x67, 0x35,
	0x32, 0x64, 0x33, 0x66, 0x64, 0x70, 0x62, 0x0a, 0x78, 0x6c, 0x71, 0x4a, 0x4d, 0x42, 0x38, 0x47,
	0x41, 0x31, 0x55, 0x64, 0x49, 0x77, 0x51, 0x59, 0x4d, 0x42, 0x61, 0x41, 0x46, 0x45, 0x77, 0x37,
	0x67, 0x4b, 0x32, 0x4c, 0x34, 0x48, 0x2f, 0x79, 0x31, 0x52, 0x4a, 0x67, 0x35, 0x32, 0x64, 0x33,
	0x66, 0x64, 0x70, 0x62, 0x78, 0x6c, 0x71, 0x4a, 0x4d, 0x41, 0x38, 0x47, 0x41, 0x31, 0x55, 0x64,
	0x45, 0x77, 0x51, 0x49, 0x4d, 0x41, 0x59, 0x42, 0x0a, 0x41, 0x66, 0x38, 0x43, 0x41, 0x51, 0x41,
	0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45,
	0x46, 0x42, 0x51, 0x41, 0x44, 0x67, 0x59, 0x45, 0x41, 0x58, 0x46, 0x4c, 0x73, 0x70, 0x52, 0x47,
	0x67, 0x2b, 0x53, 0x4b, 0x73, 0x6f, 0x44, 0x33, 0x72, 0x48, 0x4c, 0x68, 0x47, 0x32, 0x31, 0x2f,
	0x74, 0x31, 0x64, 0x64, 0x6b, 0x6b, 0x4e, 0x70, 0x54, 0x0a, 0x78, 0x56, 0x4d, 0x31, 0x66, 0x4b,
	0x61, 0x33, 0x6a, 0x45, 0x45, 0x35, 0x7a, 0x62, 0x6d, 0x6d, 0x4a, 0x74, 0x67, 0x4e, 0x51, 0x47,
	0x33, 0x68, 0x59, 0x41, 0x77, 0x69, 0x2f, 0x71, 0x6e, 0x77, 0x57, 0x72, 0x34, 0x39, 0x55, 0x6d,
	0x41, 0x57, 0x49, 0x68, 0x5a, 0x7a, 0x43, 0x43, 0x44, 0x51, 0x53, 0x2f, 0x62, 0x30, 0x65, 0x31,
	0x32, 0x6c, 0x5a, 0x2b, 0x6e, 0x65, 0x33, 0x32, 0x77, 0x4b, 0x0a, 0x4e, 0x45, 0x2b, 0x73, 0x35,
	0x6c, 0x31, 0x53, 0x71, 0x52, 0x41, 0x51, 0x46, 0x36, 0x4e, 0x68, 0x75, 0x4f, 0x35, 0x4d, 0x37,
	0x6d, 0x51, 0x32, 0x4a, 0x47, 0x38, 0x69, 0x6e, 0x48, 0x6e, 0x4e, 0x34, 0x59, 0x77, 0x70, 0x72,
	0x66, 0x74, 0x45, 0x6f, 0x67, 0x76, 0x52, 0x2f, 0x69, 0x7a, 0x65, 0x61, 0x6b, 0x35, 0x4d, 0x4d,
	0x7a, 0x43, 0x77, 0x36, 0x34, 0x44, 0x66, 0x44, 0x43, 0x79, 0x41, 0x0a, 0x6d, 0x72, 0x4a, 0x6f,
	0x71, 0x77, 0x75, 0x71, 0x69, 0x74, 0x34, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e,
	0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d,
	0x2d, 0x2d, 0x0a};

// array size is 912
static const unsigned char terminal_key_v388[] = {
	0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
	0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43,
	0x64, 0x51, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
	0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43, 0x41, 0x6c, 0x38, 0x77,
	0x67, 0x67, 0x4a, 0x62, 0x41, 0x67, 0x45, 0x41, 0x41, 0x6f, 0x47, 0x42, 0x41, 0x4e, 0x78, 0x6b,
	0x7a, 0x67, 0x50, 0x79, 0x64, 0x57, 0x34, 0x67, 0x4a, 0x36, 0x70, 0x63, 0x0a, 0x71, 0x64, 0x2b,
	0x67, 0x6c, 0x4d, 0x46, 0x4f, 0x51, 0x58, 0x48, 0x58, 0x4b, 0x76, 0x65, 0x7a, 0x47, 0x6b, 0x38,
	0x4f, 0x34, 0x54, 0x46, 0x48, 0x30, 0x36, 0x6c, 0x31, 0x74, 0x39, 0x75, 0x64, 0x34, 0x71, 0x68,
	0x79, 0x74, 0x7a, 0x36, 0x34, 0x56, 0x38, 0x52, 0x4d, 0x63, 0x2f, 0x53, 0x65, 0x35, 0x36, 0x63,
	0x76, 0x74, 0x65, 0x4f, 0x72, 0x39, 0x62, 0x76, 0x43, 0x39, 0x7a, 0x61, 0x58, 0x0a, 0x77, 0x42,
	0x77, 0x31, 0x2b, 0x78, 0x46, 0x70, 0x53, 0x78, 0x30, 0x46, 0x31, 0x70, 0x44, 0x61, 0x43, 0x4f,
	0x79, 0x5a, 0x46, 0x56, 0x57, 0x4c, 0x4e, 0x75, 0x58, 0x36, 0x6c, 0x79, 0x77, 0x43, 0x73, 0x69,
	0x43, 0x53, 0x54, 0x67, 0x52, 0x30, 0x51, 0x6e, 0x78, 0x54, 0x76, 0x44, 0x78, 0x63, 0x75, 0x64,
	0x6b, 0x45, 0x69, 0x32, 0x55, 0x71, 0x63, 0x31, 0x48, 0x49, 0x56, 0x79, 0x6d, 0x72, 0x0a, 0x72,
	0x4d, 0x73, 0x68, 0x4a, 0x4e, 0x67, 0x38, 0x61, 0x66, 0x38, 0x57, 0x6f, 0x6e, 0x69, 0x48, 0x59,
	0x6f, 0x54, 0x37, 0x2b, 0x79, 0x46, 0x44, 0x35, 0x36, 0x58, 0x64, 0x41, 0x67, 0x4d, 0x42, 0x41,
	0x41, 0x45, 0x43, 0x67, 0x59, 0x42, 0x32, 0x38, 0x53, 0x78, 0x42, 0x38, 0x78, 0x4d, 0x76, 0x47,
	0x4d, 0x6c, 0x76, 0x5a, 0x73, 0x30, 0x43, 0x39, 0x46, 0x33, 0x7a, 0x71, 0x54, 0x45, 0x6d, 0x0a,
	0x71, 0x42, 0x48, 0x33, 0x56, 0x6b, 0x43, 0x48, 0x6c, 0x43, 0x63, 0x79, 0x65, 0x6d, 0x6f, 0x66,
	0x31, 0x58, 0x6b, 0x68, 0x58, 0x43, 0x63, 0x62, 0x38, 0x37, 0x55, 0x7a, 0x63, 0x7a, 0x64, 0x69,
	0x71, 0x45, 0x51, 0x59, 0x4b, 0x37, 0x34, 0x6e, 0x65, 0x31, 0x63, 0x31, 0x68, 0x50, 0x4e, 0x38,
	0x6e, 0x71, 0x37, 0x65, 0x56, 0x71, 0x32, 0x47, 0x54, 0x42, 0x65, 0x76, 0x67, 0x41, 0x31, 0x7a,
	0x0a, 0x79, 0x38, 0x4f, 0x49, 0x31, 0x33, 0x64, 0x6c, 0x51, 0x75, 0x30, 0x6f, 0x71, 0x5a, 0x4c,
	0x41, 0x65, 0x70, 0x43, 0x6f, 0x2b, 0x78, 0x4d, 0x38, 0x6a, 0x66, 0x56, 0x66, 0x55, 0x4a, 0x6b,
	0x30, 0x35, 0x74, 0x64, 0x42, 0x76, 0x65, 0x64, 0x4e, 0x70, 0x4c, 0x58, 0x6d, 0x55, 0x6f, 0x6a,
	0x36, 0x7a, 0x4b, 0x74, 0x63, 0x45, 0x75, 0x57, 0x6d, 0x4c, 0x75, 0x55, 0x53, 0x2f, 0x31, 0x4f,
	0x45, 0x0a, 0x72, 0x59, 0x56, 0x58, 0x62, 0x71, 0x35, 0x58, 0x70, 0x4a, 0x65, 0x4d, 0x62, 0x59,
	0x4f, 0x54, 0x34, 0x51, 0x4a, 0x42, 0x41, 0x50, 0x75, 0x63, 0x59, 0x63, 0x43, 0x52, 0x39, 0x32,
	0x46, 0x45, 0x71, 0x46, 0x4e, 0x74, 0x78, 0x56, 0x78, 0x4f, 0x50, 0x79, 0x59, 0x53, 0x63, 0x53,
	0x2f, 0x7a, 0x44, 0x70, 0x6e, 0x54, 0x41, 0x6c, 0x49, 0x59, 0x38, 0x44, 0x45, 0x6b, 0x51, 0x4d,
	0x74, 0x39, 0x0a, 0x79, 0x5a, 0x39, 0x32, 0x6c, 0x79, 0x2f, 0x78, 0x66, 0x72, 0x6a, 0x42, 0x31,
	0x4e, 0x52, 0x73, 0x57, 0x6c, 0x7a, 0x79, 0x4d, 0x75, 0x69, 0x66, 0x6c, 0x2b, 0x53, 0x68, 0x5a,
	0x32, 0x30, 0x36, 0x2f, 0x63, 0x4b, 0x5a, 0x79, 0x67, 0x38, 0x4e, 0x75, 0x4c, 0x6b, 0x43, 0x51,
	0x51, 0x44, 0x67, 0x50, 0x51, 0x52, 0x4b, 0x2f, 0x34, 0x42, 0x62, 0x49, 0x42, 0x79, 0x42, 0x47,
	0x71, 0x2b, 0x72, 0x0a, 0x6e, 0x62, 0x73, 0x6a, 0x4f, 0x76, 0x2f, 0x79, 0x70, 0x48, 0x36, 0x36,
	0x49, 0x44, 0x68, 0x73, 0x2f, 0x42, 0x62, 0x62, 0x6e, 0x42, 0x55, 0x59, 0x4d, 0x59, 0x2b, 0x6b,
	0x2b, 0x7a, 0x38, 0x2f, 0x37, 0x6e, 0x34, 0x32, 0x62, 0x61, 0x74, 0x4e, 0x79, 0x47, 0x66, 0x43,
	0x4a, 0x36, 0x55, 0x56, 0x35, 0x76, 0x62, 0x52, 0x64, 0x73, 0x47, 0x7a, 0x6a, 0x73, 0x61, 0x4f,
	0x6b, 0x65, 0x44, 0x64, 0x0a, 0x64, 0x62, 0x78, 0x46, 0x41, 0x6b, 0x41, 0x65, 0x77, 0x59, 0x66,
	0x6c, 0x62, 0x54, 0x4a, 0x4c, 0x44, 0x6f, 0x52, 0x77, 0x35, 0x6b, 0x73, 0x6f, 0x74, 0x46, 0x76,
	0x64, 0x77, 0x49, 0x56, 0x62, 0x63, 0x68, 0x2b, 0x48, 0x79, 0x42, 0x5a, 0x52, 0x69, 0x4d, 0x44,
	0x62, 0x6b, 0x4f, 0x33, 0x6d, 0x73, 0x66, 0x4b, 0x53, 0x62, 0x6f, 0x47, 0x65, 0x6c, 0x36, 0x75,
	0x41, 0x31, 0x42, 0x69, 0x36, 0x0a, 0x2b, 0x70, 0x2b, 0x31, 0x47, 0x74, 0x6f, 0x45, 0x4f, 0x33,
	0x58, 0x71, 0x75, 0x5a, 0x77, 0x56, 0x36, 0x77, 0x38, 0x58, 0x32, 0x59, 0x71, 0x73, 0x65, 0x6d,
	0x58, 0x70, 0x41, 0x6b, 0x41, 0x4c, 0x39, 0x78, 0x57, 0x49, 0x6a, 0x76, 0x4f, 0x68, 0x61, 0x53,
	0x67, 0x38, 0x4e, 0x31, 0x6c, 0x53, 0x56, 0x66, 0x74, 0x4c, 0x57, 0x50, 0x57, 0x5a, 0x55, 0x2b,
	0x2b, 0x7a, 0x4c, 0x4d, 0x37, 0x31, 0x0a, 0x2b, 0x63, 0x6d, 0x61, 0x64, 0x45, 0x79, 0x6e, 0x32,
	0x74, 0x55, 0x6f, 0x58, 0x34, 0x4f, 0x7a, 0x66, 0x4a, 0x31, 0x64, 0x65, 0x43, 0x4b, 0x67, 0x35,
	0x75, 0x57, 0x71, 0x64, 0x59, 0x55, 0x59, 0x6b, 0x78, 0x64, 0x75, 0x65, 0x2b, 0x44, 0x41, 0x48,
	0x51, 0x6b, 0x35, 0x53, 0x45, 0x31, 0x77, 0x59, 0x37, 0x7a, 0x6c, 0x41, 0x6b, 0x42, 0x47, 0x37,
	0x67, 0x4e, 0x71, 0x74, 0x78, 0x35, 0x30, 0x0a, 0x38, 0x4a, 0x4d, 0x61, 0x44, 0x4b, 0x71, 0x6d,
	0x46, 0x6c, 0x62, 0x33, 0x56, 0x46, 0x42, 0x47, 0x52, 0x34, 0x58, 0x54, 0x53, 0x77, 0x70, 0x75,
	0x48, 0x31, 0x4f, 0x38, 0x66, 0x37, 0x74, 0x62, 0x5a, 0x4a, 0x76, 0x74, 0x4b, 0x5a, 0x36, 0x56,
	0x56, 0x65, 0x4b, 0x58, 0x2b, 0x2b, 0x6b, 0x41, 0x2b, 0x62, 0x70, 0x41, 0x59, 0x4b, 0x76, 0x41,
	0x58, 0x45, 0x43, 0x34, 0x38, 0x6b, 0x39, 0x77, 0x0a, 0x58, 0x6f, 0x59, 0x64, 0x57, 0x48, 0x56,
	0x79, 0x62, 0x4e, 0x68, 0x38, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50,
	0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a};

static void prepareCerts()
{
	// data_jp/network/certs
	FILE *file2 = fopen("./data_jp/network/certs/terminal-cert_v388.pem", "wb");
	fwrite(terminal_cert_v388, 1, sizeof(terminal_cert_v388), file2);
	fclose(file2);

	FILE *cacert = fopen("./data_jp/network/certs/v388-ca-cert.pem", "wb");
	fwrite(v388_ca_cert, 1, sizeof(v388_ca_cert), cacert);
	fclose(cacert);

	FILE *termkey = fopen("./data_jp/network/private/terminal-key_v388.pem", "wb");
	fwrite(terminal_key_v388, 1, sizeof(terminal_key_v388), termkey);
	fclose(termkey);

	return;
}

static HWND mt6Hwnd;

typedef BOOL(WINAPI *ShowWindow_t)(HWND, int);
static ShowWindow_t pShowWindow;

// Hello Win32 my old friend...
typedef LRESULT(WINAPI *WindowProcedure_t)(HWND, UINT, WPARAM, LPARAM);
static WindowProcedure_t pMaxituneWndProc;

static BOOL gotWindowSize = FALSE;

static LRESULT Hook_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (!gotWindowSize)
	{
		mt6SetDisplayParams(hwnd);
		gotWindowSize = TRUE;
	}

	if (msg == WM_LBUTTONDOWN ||
		msg == WM_LBUTTONUP)
	{
		mt6SetTouchData(lParam, msg == WM_LBUTTONDOWN, false);
		return 0;
	}

	if (msg == WM_POINTERDOWN ||
		msg == WM_POINTERUP)
	{
		mt6SetTouchData(lParam, msg == WM_POINTERDOWN, true);
		return 0;
	}

	return pMaxituneWndProc(hwnd, msg, wParam, lParam);
}

static BOOL Hook_ShowWindow(HWND hwnd, int nCmdShow)
{
	SetWindowLongPtrW(hwnd, -4, (LONG_PTR)Hook_WndProc);
	ShowCursor(1);

	mt6Hwnd = hwnd;
	return pShowWindow(hwnd, nCmdShow);
}

static InitFunction Wmmt6RRFunc([]()
								{
		if (std::filesystem::exists(".\\card.ini")) {
			std::remove(".\\card.ini");
		}

		char generatedAccessCode[34] = "000000000000000000000000000000000";
		strcpy(generatedAccessCode, config["Banapass"]["AccessCode"].c_str());
		WritePrivateProfileStringA("card", "accessCode", generatedAccessCode, ".\\card.ini");
		char generatedChipId[22] = "000000000000000000000";
		strcpy(generatedChipId, config["Banapass"]["Card ID"].c_str());
		WritePrivateProfileStringA("card", "chipId", generatedChipId, ".\\card.ini");

		// folder for path redirections
		prepareCerts();
		CreateDirectoryA(".\\TP", nullptr);

		bool isTerminal = false;
		if (ToBool(config["General"]["TerminalMode"]))
		{
			isTerminal = true;
		}

		std::string networkip = config["General"]["NetworkAdapterIP"];
		if (!networkip.empty())
		{
			ipaddr = networkip.c_str();
		}

		hookPort = "COM3";
		imageBase = (uintptr_t)GetModuleHandleA(0);
		MH_Initialize();
		// Hook dongle funcs
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_write", Hook_hasp_write, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_read", Hook_hasp_read, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_get_size", Hook_hasp_get_size, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_decrypt", Hook_hasp_decrypt, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_encrypt", Hook_hasp_encrypt, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_logout", Hook_hasp_logout, NULL);
		MH_CreateHookApi(L"hasp_windows_x64_30382.dll", "hasp_login", Hook_hasp_login, NULL);
		MH_CreateHookApi(L"WS2_32", "bind", Hook_bind, reinterpret_cast<LPVOID*>(&pbind));
		MH_CreateHookApi(L"dnsapi.dll", "DnsQuery_A", DnsQuery_AHook, (void**)&g_origDnsQuery_A);
		MH_CreateHookApi(L"dnsapi.dll", "DnsQueryEx", DnsQueryExHook, (void**)&g_origDnsQueryEx);
		MH_CreateHookApi(L"ws2_32.dll", "getaddrinfo", getaddrinfoHook, (void**)&g_origgetaddrinfo);
		MH_CreateHookApi(L"ws2_32.dll", "InetPtonW", InetPtonWHook, (void**)&g_origInetPtonW);

		MH_CreateHookApi(L"kernel32", "OutputDebugStringA", Hook_OutputDebugStringA, NULL);
		// CreateFile* hooks are in the JVS FILE

		// Give me the HWND please maxitune
		MH_CreateHookApi(L"user32", "ShowWindow", Hook_ShowWindow, reinterpret_cast<LPVOID*>(&pShowWindow));

		// Hook the window procedure
		// (The image starts at 0x140000000)
		pMaxituneWndProc = (WindowProcedure_t)(imageBase + 0xCBD370);

		GenerateDongleData(isTerminal);

		// Best LAN setting by doomertheboomer
		injector::WriteMemory<BYTE>(imageBase + 0xB41DAA, 0xEB, true); //content router patch
		injector::MakeNOP(imageBase + 0x732F36, 2, true);

		// resolves a system error
		injector::WriteMemory<uint8_t>(hook::get_pattern("0F 94 C0 84 C0 0F 94 C0 84 C0 75 05 45 32 ? EB", 0x13), 0, true);

		// Skip weird camera init that stucks entire pc on certain brands. TESTED ONLY ON 05!!!!
		if (ToBool(config["General"]["WhiteScreenFix"]))
		{
			injector::WriteMemory<DWORD>(hook::get_pattern("48 8B C4 55 57 41 54 41 55 41 56 48 8D 68 A1 48 81 EC 90 00 00 00 48 C7 45 D7 FE FF FF FF 48 89 58 08 48 89 70 18 45 33 F6 4C 89 75 DF 33 C0 48 89 45 E7", 0), 0x90C3C032, true);
		}

		auto location = hook::get_pattern<char>("48 83 EC 28 33 D2 B9 70 00 02 00 E8 ? ? ? ? 85 C0 79 06");

		// First auth error skip
		//injector::WriteMemory<BYTE>(imageBase + 0x6A0077, 0xEB, true);

		if (isTerminal)
		{
			// More dongle error shit?
			injector::MakeNOP((imageBase + 0x73B905), 5);
			safeJMP(hook::get_pattern("8B 01 0F B6 40 78 C3 CC CC CC CC"), ReturnTrue);
		}
		else
		{
			// Terminal on same machine check.
			injector::MakeNOP(hook::get_pattern("74 ? 80 7B 31 00 75 ? 48 8B 43 10 80 78 31 00 75 1A 48 8B D8 48 8B 00 80 78 31 00 75 ? 48 8B D8"), 2);
		}

		// Enable all print
		injector::WriteMemory<BYTE>(imageBase + 0x9BD193, 0xEB, true);
		injector::WriteMemory<BYTE>(imageBase + 0x7420F9, 0, true);

		// Safenet check
		injector::MakeNOP((imageBase + 0x9C800F), 2);

		// Dongle crap
		injector::WriteMemory<BYTE>(imageBase + 0x741F34, 0xEB, true);
		injector::WriteMemory<WORD>(imageBase + 0x742A53, 0xE990, true);

		// Skip error modals
		//injector::MakeNOP(imageBase + 0x7089F4, 2);

		// path fixes
		injector::WriteMemoryRaw(imageBase + 0x14B6618, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6638, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6658, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6678, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6698, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B66B8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B66D8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B66F8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6718, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6730, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6748, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x14B6768, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B0E8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B100, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B118, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B140, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B168, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B180, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B198, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B1A8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B1B8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B1D0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B1E8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B208, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B228, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B238, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B248, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B260, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B278, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B290, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B2A8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B2C0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B2D8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B2F0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B308, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x156B320, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1573878, "TP", 2, true); // F:/contents/
		injector::WriteMemoryRaw(imageBase + 0x15C2450, "TP/contents/", 12, true); // F:contents/
		injector::WriteMemoryRaw(imageBase + 0x15C2460, "TP/contents/", 12, true);	// G:contents/
		injector::WriteMemoryRaw(imageBase + 0x16232B0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x16232C8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624008, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624020, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624038, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624060, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624088, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x16240A0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624AC0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1624AE0, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1623FDC, "TP", 2, true); // F:/
		injector::WriteMemoryRaw(imageBase + 0x1575EA8, "TP", 2, true);
		injector::WriteMemoryRaw(imageBase + 0x1576678, "TP", 2, true);

		// Fix dongle error (can be triggered by various USB hubs, dongles
		injector::MakeNOP(imageBase + 0x9C800F, 2, true);

		// Fix crash when saving story mode and Time attack, if the error isn't handled then it doesnt crash?????
		injector::WriteMemory<uint8_t>(imageBase + 0x9AD3AF, 0xEB, true);
		injector::WriteMemory<uint8_t>(imageBase + 0x9AD338, 0x38EB, true);

		//init_BanapassEmu();
		MH_EnableHook(MH_ALL_HOOKS); }, GameID::WMMT6RR);
#endif
#pragma optimize("", on).