{
  BOOL   fRet = FALSE;
  HANDLE hToken = NULL;

  if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  {
    TOKEN_ELEVATION Elevation;
    DWORD cbSize = sizeof( TOKEN_ELEVATION );

    if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof( Elevation), &cbSize))
    {
      fRet = Elevation.TokenIsElevated;
    }
  }

  if (hToken)
  {
    CloseHandle(hToken);
  }

  return fRet;
}

/*-------------------------------------------------------------------------------
---------------------------------------------------------------------------------*/

//changes 0.96 -> 0.97: speed variable is global, detects bumpers, all timed (no enter), lbumper speed toggler
//changes 0.97 -> 0.98: performance improvements, operational volume function, shorter beeps, no XY text
//changes 0.98 -> 0.985: 144Hz, Y to hide window(added float stillHoldingY), code cleanup, comments added
//changes 0.985 -> 0.986: Adding configuration file, changing from beeps to vibration.
//changes 0.986 -> 0.989: Improved speeds and speed reporting, created automatic config generator!
//TODO FOR FUTURE VERSIONS - offload speeds into config file

#include <windows.h> // for Beep()
#include <iostream>

#pragma comment(lib, "XInput9_1_0.lib")
#pragma comment(lib, "winmm") // for volume

#include "Gopher.h"

bool ChangeVolume(double nVolume, bool bScalar); // Not used yet
BOOL isRunningAsAdministrator(); // Check if administrator, makes on-screen keyboard clickable

/* TODO:
 * Enable/disable button
 * Key Codes:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
 * xinput:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinput_gamepad%28v=vs.85%29.aspx
 */

int main()
{
  CXBOXController controller(1);
  Gopher gopher(&controller);
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleTitle( TEXT( "Gopher360" ) );

  system("Color 1D");

  printf("Welcome to Gopher360 - a VERY fast and lightweight controller-to-keyboard & mouse input tool.\n");
  printf("All you need is an Xbox360/Xbone controller (wired or wireless adapter), or DualShock (with InputMapper 1.5+)\n");
  printf("Gopher will autofind the xinput device and begin reading input - if nothing happens, verify connectivity.\n");
  printf("See the GitHub repository at bit.ly/1syAhMT for more info. Twitter contact: TylerAt60FPS\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 23);
  printf("Gopher is free (as in freedom) software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n");
  printf("\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see http://www.gnu.org/licenses/.");
  SetConsoleTextAttribute(hConsole, 29);
  printf("\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 5); // set color to purple on black (windows only)

  // dump important tips
  printf("Tip - Press left and right bumpers simultaneously to toggle speeds! (Default is left and right bumpers, configurable in config.ini)\n");

  if (!isRunningAsAdministrator())
  {
    printf("Tip - Not running as an admin! Windows on-screen keyboard and others won't work without admin rights.\n");
  }

  gopher.loadConfigFile();

  // Start the Gopher program loop
  while (true)
  {
    gopher.loop();
  }
}

BOOL isRunningAsAdministrator()

// This works, but it's not enabled in the software since the best button for it is still undecided
bool ChangeVolume(double nVolume, bool bScalar) //o b
{
  HRESULT hr = NULL;
  bool decibels = false;
  bool scalar = false;
  double newVolume = nVolume;

  CoInitialize(NULL);
  IMMDeviceEnumerator *deviceEnumerator = NULL;
  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
                        __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
  IMMDevice *defaultDevice = NULL;

  hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
  deviceEnumerator->Release();
  deviceEnumerator = NULL;

  IAudioEndpointVolume *endpointVolume = NULL;
  hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
                               CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
  defaultDevice->Release();
  defaultDevice = NULL;

  float currentVolume = 0;
  endpointVolume->GetMasterVolumeLevel(&currentVolume);
  //printf("Current volume in dB is: %f\n", currentVolume);

  hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);

  // printf("Current volume as a scalar is: %f\n", currentVolume);
  if (bScalar == false)
  {
    hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);
  }
  else if (bScalar == true)
  {
    hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);
  }
  endpointVolume->Release();

  CoUninitialize();

  return FALSE;
}


evice and begin reading input - if nothing happens, verify connectivity.\n");

/*-------------------------------------------------------------------------------
---------------------------------------------------------------------------------*/

//changes 0.96 -> 0.97: speed variable is global, detects bumpers, all timed (no enter), lbumper speed toggler
//changes 0.97 -> 0.98: performance improvements, operational volume function, shorter beeps, no XY text
//changes 0.98 -> 0.985: 144Hz, Y to hide window(added float stillHoldingY), code cleanup, comments added
//changes 0.985 -> 0.986: Adding configuration file, changing from beeps to vibration.
//changes 0.986 -> 0.989: Improved speeds and speed reporting, created automatic config generator!
//TODO FOR FUTURE VERSIONS - offload speeds into config file

#include <windows.h> // for Beep()
#include <iostream>

#pragma comment(lib, "XInput9_1_0.lib")
#pragma comment(lib, "winmm") // for volume

#include "Gopher.h"

bool ChangeVolume(double nVolume, bool bScalar); // Not used yet
BOOL isRunningAsAdministrator(); // Check if administrator, makes on-screen keyboard clickable

/* TODO:
 * Enable/disable button
 * Key Codes:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
 * xinput:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinput_gamepad%28v=vs.85%29.aspx
 */

int main()
{
  CXBOXController controller(1);
  Gopher gopher(&controller);
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleTitle( TEXT( "Gopher360" ) );

  system("Color 1D");

  printf("Welcome to Gopher360 - a VERY fast and lightweight controller-to-keyboard & mouse input tool.\n");
  printf("All you need is an Xbox360/Xbone controller (wired or wireless adapter), or DualShock (with InputMapper 1.5+)\n");
  printf("Gopher will autofind the xinput d

  printf("See the GitHub repository at bit.ly/1syAhMT for more info. Twitter contact: TylerAt60FPS\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 23);
  printf("Gopher is free (as in freedom) software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n");
  printf("\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see http://www.gnu.org/licenses/.");
  SetConsoleTextAttribute(hConsole, 29);
  printf("\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 5); // set color to purple on black (windows only)

  // dump important tips
  printf("Tip - Press left and right bumpers simultaneously to toggle speeds! (Default is left and right bumpers, configurable in config.ini)\n");

  if (!isRunningAsAdministrator())
  {
    printf("Tip - Not running as an admin! Windows on-screen keyboard and others won't work without admin rights.\n");
  }

  gopher.loadConfigFile();

  // Start the Gopher program loop
  while (true)
  {
    gopher.loop();
  }
}

BOOL isRunningAsAdministrator()
{
  BOOL   fRet = FALSE;
  HANDLE hToken = NULL;

  if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  {
    TOKEN_ELEVATION Elevation;
    DWORD cbSize = sizeof( TOKEN_ELEVATION );

    if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof( Elevation), &cbSize))
    {
      fRet = Elevation.TokenIsElevated;
    }
  }

  if (hToken)
  {
    CloseHandle(hToken);
  }

  return fRet;
}

// This works, but it's not enabled in the software since the best button for it is still undecided
bool ChangeVolume(double nVolume, bool bScalar) //o b
{
  HRESULT hr = NULL;
  bool decibels = false;
  bool scalar = false;
  double newVolume = nVolume;

  CoInitialize(NULL);
  IMMDeviceEnumerator *deviceEnumerator = NULL;
  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
                        __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
  IMMDevice *defaultDevice = NULL;

  hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
  deviceEnumerator->Release();
  deviceEnumerator = NULL;

  IAudioEndpointVolume *endpointVolume = NULL;
  hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
                               CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
  defaultDevice->Release();
  defaultDevice = NULL;

  float currentVolume = 0;
  endpointVolume->GetMasterVolumeLevel(&currentVolume);
  //printf("Current volume in dB is: %f\n", currentVolume);

  hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);

  // printf("Current volume as a scalar is: %f\n", currentVolume);
  if (bScalar == false)
  {
    hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);
  }
  else if (bScalar == true)
  {
    hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);
  }
  endpointVolume->Release();

  CoUninitialize();

  return FALSE;
}


/*-------------------------------------------------------------------------------
---------------------------------------------------------------------------------*/

//changes 0.96 -> 0.97: speed variable is global, detects bumpers, all timed (no enter), lbumper speed toggler
//changes 0.97 -> 0.98: performance improvements, operational volume function, shorter beeps, no XY text
//changes 0.98 -> 0.985: 144Hz, Y to hide window(added float stillHoldingY), code cleanup, comments added
//changes 0.985 -> 0.986: Adding configuration file, changing from beeps to vibration.
//changes 0.986 -> 0.989: Improved speeds and speed reporting, created automatic config generator!
//TODO FOR FUTURE VERSIONS - offload speeds into config file

#include <windows.h> // for Beep()
#include <iostream>

#pragma comment(lib, "XInput9_1_0.lib")
#pragma comment(lib, "winmm") // for volume

#include "Gopher.h"

bool ChangeVolume(double nVolume, bool bScalar); // Not used yet
BOOL isRunningAsAdministrator(); // Check if administrator, makes on-screen keyboard clickable

/* TODO:
 * Enable/disable button
 * Key Codes:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
 * xinput:
 *   http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.reference.xinput_gamepad%28v=vs.85%29.aspx
 */

int main()
{
  CXBOXController controller(1);
  Gopher gopher(&controller);
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleTitle( TEXT( "Gopher360" ) );

  system("Color 1D");

  printf("Welcome to Gopher360 - a VERY fast and lightweight controller-to-keyboard & mouse input tool.\n");
  printf("All you need is an Xbox360/Xbone controller (wired or wireless adapter), or DualShock (with InputMapper 1.5+)\n");
  printf("Gopher will autofind the xinput device and begin reading input - if nothing happens, verify connectivity.\n");
  printf("See the GitHub repository at bit.ly/1syAhMT for more info. Twitter contact: TylerAt60FPS\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 23);
  printf("Gopher is free (as in freedom) software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n");
  printf("\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see http://www.gnu.org/licenses/.");
  SetConsoleTextAttribute(hConsole, 29);
  printf("\n\n-------------------------\n\n");

  SetConsoleTextAttribute(hConsole, 5); // set color to purple on black (windows only)

  // dump important tips
  printf("Tip - Press left and right bumpers simultaneously to toggle speeds! (Default is left and right bumpers, configurable in config.ini)\n");

  if (!isRunningAsAdministrator())
  {
    printf("Tip - Not running as an admin! Windows on-screen keyboard and others won't work without admin rights.\n");
  }

  gopher.loadConfigFile();

  // Start the Gopher program loop

}

BOOL isRunningAsAdministrator()
{
  BOOL   fRet = FALSE;
  HANDLE hToken = NULL;

  if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  {
    TOKEN_ELEVATION Elevation;
    DWORD cbSize = sizeof( TOKEN_ELEVATION );

    if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof( Elevation), &cbSize))
    {
      fRet = Elevation.TokenIsElevated;
    }
  }

  if (hToken)
  {
    CloseHandle(hToken);
  }

  return fRet;
}

// This works, but it's not enabled in the software since the best button for it is still undecided
bool ChangeVolume(double nVolume, bool bScalar) //o b
{
  HRESULT hr = NULL;
  bool decibels = false;
  bool scalar = false;
  double newVolume = nVolume;

  CoInitialize(NULL);
  IMMDeviceEnumerator *deviceEnumerator = NULL;
  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
                        __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
  IMMDevice *defaultDevice = NULL;

  hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
  deviceEnumerator->Release();
  deviceEnumerator = NULL;

  IAudioEndpointVolume *endpointVolume = NULL;
  hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
                               CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
  defaultDevice->Release();
  defaultDevice = NULL;

  float currentVolume = 0;
  endpointVolume->GetMasterVolumeLevel(&currentVolume);
  //printf("Current volume in dB is: %f\n", currentVolume);

  hr = endpointVolume->GetMasterVolumeLevelScalar(&currentVolume);

  // printf("Current volume as a scalar is: %f\n", currentVolume);
  if (bScalar == false)
  {
    hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL);
  }
  else if (bScalar == true)
  {
    hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL);
  }
  endpointVolume->Release();

  CoUninitialize();

  return FALSE;
}


bool CNoMercy::InitLauncher(HINSTANCE hInstance, const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bProtected)

#include "NM_Index.h"
#include "NM_Main.h"
#include "Defines.h"

#include <lazy_importer.hpp>

#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "Wintrust.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "MPR")
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "userenv.lib")
#pragma comment(lib, "imagehlp.lib")
#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "FltLib.lib")

std::shared_ptr <CNM_App> g_nmApp = nullptr;

#pragma optimize("", off)
void CNoMercy::InitTest(bool bClient)
{
	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Base allocation failed!\n").crypt_get());
		return;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_TESTAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Test Initilization completed!");
}

bool CNoMercy::InitCore(const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bStandaloneProcess)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_CLIENT, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	std::string szStandaloneLicense = xorstr("ABCDEF123490").crypt_get();
	if (bStandaloneProcess /* debug */ || !strcmp(c_szLicenseCode, szStandaloneLicense.c_str()) /* release */)
		g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);
	else
		g_nmApp->DataInstance()->SetAppType(NM_CLIENT);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Core Initilization completed!");
	return true;
}

bool CNoMercy::InitMasterServer(WORD wPort)
{
	static auto bClient = false;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVER);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "MServer Initilization completed!");
	return true;
}

{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_LAUNCHER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_LAUNCHER);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);
	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetMainWnd(hInstance);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Launcher Initilization completed!");
	return true;
}

void CNoMercy::OnServiceMessage(int iMessageID)
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base class not found!\n").crypt_get());
		return;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base is not initialized!\n").crypt_get());
		return;
	}

	g_nmApp->OnServiceMessageHandled(iMessageID);
}

bool CNoMercy::InitService(bool bProtected, LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVICE, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVICE);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Service Initilization completed!");
	return true;
}

bool CNoMercy::InitShadow(HMODULE hModule, HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int nCmdShow)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Base allocation failed!\n").crypt_get());
		return false;
	}

	ANTI_MODULE_INFO ami = { hModule, nullptr, NULL, NULL, NULL };
	if (g_nmApp->InitSelfProtection(&ami) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Self-protection failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SHADOW, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	if (g_nmApp->DataInstance()->IsShadowInitialized() == true)
		return false;
	g_nmApp->DataInstance()->SetShadowInitialized(true);

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SHADOW);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	g_nmApp->FunctionsInstance()->InitShadow(hWnd, hInstance, lpszCmdLine, nCmdShow);

	DEBUG_LOG(LL_SYS, "Shadow Initilization completed!");
	return true;
}

bool CNoMercy::InitHelper(LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_HELPERAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_HELPERAPP);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Helper Initilization completed!");
	return true;
}

bool CNoMercy::Release()
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Base class not found!\n").crypt_get());
		return false;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL]Base is not initialized!\n").crypt_get());
		return false;
	}

	auto dwFinalErrCode = 0UL;
	if (g_nmApp->Finalize(&dwFinalErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalize failed! Error: %u\n").crypt_get(), dwFinalErrCode);
		return false;
	}

	Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalization completed!").crypt_get());
	return true;
}

bool CNoMercy::CreateMessageHandler(TNMCallback lpMessageHandler)
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->CreateMessageHandler(lpMessageHandler);
	}

	return false;
}
bool CNoMercy::SendNMMessage(int Code, LPVOID lpMessage)
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->ProcessClientMessage(Code, lpMessage);
	}
	return false;
}

void CNoMercy::OnGameTick()
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		g_nmApp->SDKHelperInstance()->OnGameTick();
	}
}

#pragma optimize("", on)


{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->ProcessClientMessage(Code, lpMessage);
	}
	return false;
}

#include "NM_Index.h"
#include "NM_Main.h"
#include "Defines.h"

#include <lazy_importer.hpp>

#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "Wintrust.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "MPR")
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "userenv.lib")
#pragma comment(lib, "imagehlp.lib")
#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "FltLib.lib")

std::shared_ptr <CNM_App> g_nmApp = nullptr;

#pragma optimize("", off)
void CNoMercy::InitTest(bool bClient)
{
	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Base allocation failed!\n").crypt_get());
		return;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_TESTAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Test Initilization completed!");
}

bool CNoMercy::InitCore(const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bStandaloneProcess)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_CLIENT, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	std::string szStandaloneLicense = xorstr("ABCDEF123490").crypt_get();
	if (bStandaloneProcess /* debug */ || !strcmp(c_szLicenseCode, szStandaloneLicense.c_str()) /* release */)
		g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);
	else
		g_nmApp->DataInstance()->SetAppType(NM_CLIENT);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Core Initilization completed!");
	return true;
}

bool CNoMercy::InitMasterServer(WORD wPort)
{
	static auto bClient = false;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVER);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "MServer Initilization completed!");
	return true;
}

bool CNoMercy::InitLauncher(HINSTANCE hInstance, const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bProtected)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_LAUNCHER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_LAUNCHER);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);
	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetMainWnd(hInstance);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Launcher Initilization completed!");
	return true;
}

void CNoMercy::OnServiceMessage(int iMessageID)
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base class not found!\n").crypt_get());
		return;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base is not initialized!\n").crypt_get());
		return;
	}

	g_nmApp->OnServiceMessageHandled(iMessageID);
}

bool CNoMercy::InitService(bool bProtected, LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVICE, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVICE);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Service Initilization completed!");
	return true;
}

bool CNoMercy::InitShadow(HMODULE hModule, HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int nCmdShow)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Base allocation failed!\n").crypt_get());
		return false;
	}

	ANTI_MODULE_INFO ami = { hModule, nullptr, NULL, NULL, NULL };
	if (g_nmApp->InitSelfProtection(&ami) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Self-protection failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SHADOW, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	if (g_nmApp->DataInstance()->IsShadowInitialized() == true)
		return false;
	g_nmApp->DataInstance()->SetShadowInitialized(true);

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SHADOW);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	g_nmApp->FunctionsInstance()->InitShadow(hWnd, hInstance, lpszCmdLine, nCmdShow);

	DEBUG_LOG(LL_SYS, "Shadow Initilization completed!");
	return true;
}

bool CNoMercy::InitHelper(LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_HELPERAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_HELPERAPP);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Helper Initilization completed!");
	return true;
}

bool CNoMercy::Release()
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Base class not found!\n").crypt_get());
		return false;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL]Base is not initialized!\n").crypt_get());
		return false;
	}

	auto dwFinalErrCode = 0UL;
	if (g_nmApp->Finalize(&dwFinalErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalize failed! Error: %u\n").crypt_get(), dwFinalErrCode);
		return false;
	}

	Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalization completed!").crypt_get());
	return true;
}

bool CNoMercy::CreateMessageHandler(TNMCallback lpMessageHandler)
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->CreateMessageHandler(lpMessageHandler);
	}

	return false;
}
bool CNoMercy::SendNMMessage(int Code, LPVOID lpMessage)

void CNoMercy::OnGameTick()
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		g_nmApp->SDKHelperInstance()->OnGameTick();
	}
}

#pragma optimize("", on)


#include "NM_Index.h"
#include "NM_Main.h"
#include "Defines.h"

#include <lazy_importer.hpp>

#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "Wintrust.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "MPR")
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "wtsapi32.lib")
#pragma comment(lib, "userenv.lib")
#pragma comment(lib, "imagehlp.lib")
#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "FltLib.lib")

std::shared_ptr <CNM_App> g_nmApp = nullptr;

#pragma optimize("", off)
void CNoMercy::InitTest(bool bClient)
{
	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Base allocation failed!\n").crypt_get());
		return;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_TESTAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[TEST] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Test Initilization completed!");
}

bool CNoMercy::InitCore(const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bStandaloneProcess)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_CLIENT, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->

	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	std::string szStandaloneLicense = xorstr("ABCDEF123490").crypt_get();
	if (bStandaloneProcess /* debug */ || !strcmp(c_szLicenseCode, szStandaloneLicense.c_str()) /* release */)
		g_nmApp->DataInstance()->SetAppType(NM_TESTAPP);
	else
		g_nmApp->DataInstance()->SetAppType(NM_CLIENT);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[CORE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Core Initilization completed!");
	return true;
}

bool CNoMercy::InitMasterServer(WORD wPort)
{
	static auto bClient = false;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVER);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[MSERVER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "MServer Initilization completed!");
	return true;
}

bool CNoMercy::InitLauncher(HINSTANCE hInstance, const char* c_szLicenseCode, LPCVOID c_pModuleInfo, bool bProtected)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_LAUNCHER, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_LAUNCHER);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);
	g_nmApp->DataInstance()->SetLicenseCode(c_szLicenseCode);
	g_nmApp->DataInstance()->SetMainWnd(hInstance);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[LAUNCHER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Launcher Initilization completed!");
	return true;
}

void CNoMercy::OnServiceMessage(int iMessageID)
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base class not found!\n").crypt_get());
		return;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SVCMSG] Base is not initialized!\n").crypt_get());
		return;
	}

	g_nmApp->OnServiceMessageHandled(iMessageID);
}

bool CNoMercy::InitService(bool bProtected, LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SERVICE, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SERVICE);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, bProtected, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SERVICE] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Service Initilization completed!");
	return true;
}

bool CNoMercy::InitShadow(HMODULE hModule, HWND hWnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int nCmdShow)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Base allocation failed!\n").crypt_get());
		return false;
	}

	ANTI_MODULE_INFO ami = { hModule, nullptr, NULL, NULL, NULL };
	if (g_nmApp->InitSelfProtection(&ami) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Self-protection failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_SHADOW, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	if (g_nmApp->DataInstance()->IsShadowInitialized() == true)
		return false;
	g_nmApp->DataInstance()->SetShadowInitialized(true);

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_SHADOW);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[SHADOW] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	g_nmApp->FunctionsInstance()->InitShadow(hWnd, hInstance, lpszCmdLine, nCmdShow);

	DEBUG_LOG(LL_SYS, "Shadow Initilization completed!");
	return true;
}

bool CNoMercy::InitHelper(LPCVOID c_pModuleInfo)
{
	static auto bClient = true;

	g_nmApp = std::make_shared<CNM_App>();
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Base allocation failed!\n").crypt_get());
		return false;
	}

	auto dwPreInitErrCode = 0UL;
	if (g_nmApp->PreInitialize(NM_HELPERAPP, &dwPreInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Pre-Initialize failed! Error: %u\n").crypt_get(), dwPreInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(false);

	g_nmApp->DataInstance()->SetAppType(NM_HELPERAPP);
	g_nmApp->DataInstance()->SetMainThreadId(LI_FIND(GetCurrentThreadId)());
	g_nmApp->DataInstance()->SetAntiModuleInformations(c_pModuleInfo);

	auto dwInitErrCode = 0UL;
	if (g_nmApp->Initialize(bClient, false, &dwInitErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[HELPER] Initialize failed! Error: %u\n").crypt_get(), dwInitErrCode);
		return false;
	}

	g_nmApp->SetInitializeStatus(true);

	DEBUG_LOG(LL_SYS, "Helper Initilization completed!");
	return true;
}

bool CNoMercy::Release()
{
	if (!IS_VALID_SMART_PTR(g_nmApp))
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Base class not found!\n").crypt_get());
		return false;
	}

	if (g_nmApp->AppIsInitiliazed() == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL]Base is not initialized!\n").crypt_get());
		return false;
	}

	auto dwFinalErrCode = 0UL;
	if (g_nmApp->Finalize(&dwFinalErrCode) == false)
	{
		Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalize failed! Error: %u\n").crypt_get(), dwFinalErrCode);
		return false;
	}

	Logf(CUSTOM_LOG_FILENAME, xorstr("[FINAL] Finalization completed!").crypt_get());
	return true;
}

bool CNoMercy::CreateMessageHandler(TNMCallback lpMessageHandler)
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->CreateMessageHandler(lpMessageHandler);
	}

	return false;
}
bool CNoMercy::SendNMMessage(int Code, LPVOID lpMessage)
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		return g_nmApp->SDKHelperInstance()->ProcessClientMessage(Code, lpMessage);
	}
	return false;
}

void CNoMercy::OnGameTick()
{
	if (IS_VALID_SMART_PTR(g_nmApp) && IS_VALID_SMART_PTR(g_nmApp->SDKHelperInstance()))
	{
		g_nmApp->SDKHelperInstance()->OnGameTick();
	}
}

#pragma optimize("", on)


/* The following code example is taken from the book
 * "The C++ Standard Library - A Tutorial and Reference"
 * by Nicolai M. Josuttis, Addison-Wesley, 1999
 *
 */
#include "algostuff.hpp"
using namespace std;

{
    deque<int> coll;

    INSERT_ELEMENTS(coll,3,7);
    INSERT_ELEMENTS(coll,2,6);
    INSERT_ELEMENTS(coll,1,5);
    PRINT_ELEMENTS(coll);

    // extract the four lowest elements
    nth_element (coll.begin(),     // beginning of range
                 coll.begin()+3,   // element that should be sorted correctly
                 coll.end());      // end of range

    // print them
    cout << "the four lowest elements are:  ";
    copy (coll.begin(), coll.begin()+4,
          ostream_iterator<int>(cout," "));
    cout << endl;

    // extract the four highest elements
    nth_element (coll.begin(),     // beginning of range
                 coll.end()-4,     // element that should be sorted correctly
                 coll.end());      // end of range

    // print them
    cout << "the four highest elements are: ";
    copy (coll.end()-4, coll.end(),
          ostream_iterator<int>(cout," "));
    cout << endl;

    // extract the four highest elements (second version)
    nth_element (coll.begin(),     // beginning of range
                 coll.begin()+3,   // element that should be sorted correctly
                 coll.end(),       // end of range
                 greater<int>());  // sorting criterion

    // print them
    cout << "the four highest elements are: ";
    copy (coll.begin(), coll.begin()+4,
          ostream_iterator<int>(cout," "));
    cout << endl;
}


/* The following code example is taken from the book
 * "The C++ Standard Library - A Tutorial and Reference"
 * by Nicolai M. Josuttis, Addison-Wesley, 1999
 *
 */
#include "algostuff.hpp"
using namespace std;

int main()
{
    deque<int> coll;

    INSERT_ELEMENTS(coll,3,7);
    INSERT_ELEMENTS(coll,2,6);
    INSERT_ELEMENTS(coll,1,5);
    PRINT_ELEMENTS(coll);

    // extract the four lowest elements
    nth_element (coll.begin(),     // beginning of range
                 coll.begin()+3,   // element that should be sorted correctly
                 coll.end());      // end of range

    // print them
    cout << "the four lowest

    copy (coll.begin(), coll.begin()+4,
          ostream_iterator<int>(cout," "));
    cout << endl;

    // extract the four highest elements
    nth_element (coll.begin(),     // beginning of range
                 coll.end()-4,     // element that should be sorted correctly
                 coll.end());      // end of range

    // print them
    cout << "the four highest elements are: ";
    copy (coll.end()-4, coll.end(),
          ostream_iterator<int>(cout," "));
    cout << endl;

    // extract the four highest elements (second version)
    nth_element (coll.begin(),     // beginning of range
                 coll.begin()+3,   // element that should be sorted correctly
                 coll.end(),       // end of range
                 greater<int>());  // sorting criterion

    // print them
    cout << "the four highest elements are: ";
    copy (coll.begin(), coll.begin()+4,
          ostream_iterator<int>(cout," "));
    cout << endl;
}


QString getNonDef(QDoubleSpinBox *sb, const QString &def)

#include "view-helper.h"

#include "utilities/strings.h"

void utils::toggle(MainWindow *mainWindow, QAction *action,
	FadeGroupBox *groupBox, bool parsingVmt)
{
	if(action->isEnabled()) {
		if(groupBox->isVisible()) {
			groupBox->fadeHide(mainWindow);
			action->setChecked(false);
		} else {
			groupBox->show();
			if (!parsingVmt) {
				groupBox->fadeShow(mainWindow);
				groupBox->repaint();
			}
			action->setChecked(true);
		}
	}
	mainWindow->refreshRequested();
}

void utils::toggle(MainWindow *mainWindow, bool checked, FadeGroupBox *groupBox,
		bool parsingVmt)
{
	if(checked) {
		groupBox->show();
		if (!parsingVmt)
			groupBox->fadeShow(mainWindow);
		groupBox->repaint();

	} else {
		groupBox->fadeHide(mainWindow);
	}
	mainWindow->refreshRequested();
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, TintSlider *sliderWidget,
		Ui::MainWindow *ui)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
		sliderWidget->initialize(colorWidget, color);
	}
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, Ui::MainWindow *ui, bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
	}
}

void utils::applyColor(const QString &parameter, const QString &value,
	QToolButton *colorWidget, QDoubleSpinBox *spinBox, Ui::MainWindow *ui,
	bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {

		float red = r.doubleValues.at(0);
		float green = r.doubleValues.at(1);
		float blue = r.doubleValues.at(2);

		double max = static_cast<double>(qMax(red, qMax(green, blue)));

		red = red / max;
		green = green / max;
		blue = blue / max;

		utils::applyBackgroundColor(red * 255, green * 255, blue * 255,
			colorWidget);
		spinBox->setValue(max);
	}
}

void utils::applyBackgroundColor(int red, int green, int blue,
	QToolButton *colorWidget)
{
	// according to the documentation the cast to the string is not
	// required but we seem to need it
	QString s = QString("background-color: rgb(%1, %2, %3)")
		.arg(STR(red), STR(green), STR(blue));
	colorWidget->setStyleSheet(s);
}

QColor utils::getBG(QToolButton *widget)
{
	// TODO: Debug and explain how and why this works!
	QColor result;

	QString ss = widget->styleSheet();
	ss = ss.mid(ss.indexOf('(') + 1, ss.length() - ss.indexOf('(') - 2);

	QStringList list = ss.split(',');
	result.setRed(list.at(0).toInt());
	result.setGreen(list.at(1).toInt());
	result.setBlue(list.at(2).toInt());

	return result;
}

namespace utils {

{
	if (!sb->isEnabled())
		return "";

	const QString text = STR(sb->value());
	if (stripZeroes(text) == def)
		return "";

	return text;
}

} // namespace utils


{
	// according to the documentation the cast to the string is not
	// required but we seem to need it
	QString s = QString("background-color: rgb(%1, %2, %3)")
		.arg(STR(red), STR(green), STR(blue));
	colorWidget->setStyleSheet(s);
}

#include "view-helper.h"

#include "utilities/strings.h"

void utils::toggle(MainWindow *mainWindow, QAction *action,
	FadeGroupBox *groupBox, bool parsingVmt)
{
	if(action->isEnabled()) {
		if(groupBox->isVisible()) {
			groupBox->fadeHide(mainWindow);
			action->setChecked(false);
		} else {
			groupBox->show();
			if (!parsingVmt) {
				groupBox->fadeShow(mainWindow);
				groupBox->repaint();
			}
			action->setChecked(true);
		}
	}
	mainWindow->refreshRequested();
}

void utils::toggle(MainWindow *mainWindow, bool checked, FadeGroupBox *groupBox,
		bool parsingVmt)
{
	if(checked) {
		groupBox->show();
		if (!parsingVmt)
			groupBox->fadeShow(mainWindow);
		groupBox->repaint();

	} else {
		groupBox->fadeHide(mainWindow);
	}
	mainWindow->refreshRequested();
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, TintSlider *sliderWidget,
		Ui::MainWindow *ui)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
		sliderWidget->initialize(colorWidget, color);
	}
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, Ui::MainWindow *ui, bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
	}
}

void utils::applyColor(const QString &parameter, const QString &value,
	QToolButton *colorWidget, QDoubleSpinBox *spinBox, Ui::MainWindow *ui,
	bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {

		float red = r.doubleValues.at(0);
		float green = r.doubleValues.at(1);
		float blue = r.doubleValues.at(2);

		double max = static_cast<double>(qMax(red, qMax(green, blue)));

		red = red / max;
		green = green / max;
		blue = blue / max;

		utils::applyBackgroundColor(red * 255, green * 255, blue * 255,
			colorWidget);
		spinBox->setValue(max);
	}
}

void utils::applyBackgroundColor(int red, int green, int blue,
	QToolButton *colorWidget)

QColor utils::getBG(QToolButton *widget)
{
	// TODO: Debug and explain how and why this works!
	QColor result;

	QString ss = widget->styleSheet();
	ss = ss.mid(ss.indexOf('(') + 1, ss.length() - ss.indexOf('(') - 2);

	QStringList list = ss.split(',');
	result.setRed(list.at(0).toInt());
	result.setGreen(list.at(1).toInt());
	result.setBlue(list.at(2).toInt());

	return result;
}

namespace utils {

QString getNonDef(QDoubleSpinBox *sb, const QString &def)
{
	if (!sb->isEnabled())
		return "";

	const QString text = STR(sb->value());
	if (stripZeroes(text) == def)
		return "";

	return text;
}

} // namespace utils


#include "view-helper.h"

#include "utilities/strings.h"

void utils::toggle(MainWindow *mainWindow, QAction *action,
	FadeGroupBox *groupBox, bool parsingVmt)
{
	if(action->isEnabled()) {
		if(groupBox->isVisible()) {
			groupBox->fadeHide(mainWindow);
			action->setChecked(false);
		} else {
			groupBox->show();
			if (!parsingVmt) {
				groupBox->fadeShow(mainWindow);
				groupBox->repaint();
			}
			action->setChecked(true);
		}
	}
	mainWindow->refreshRequested();
}

void utils::toggle(MainWindow *mainWindow, bool checked, FadeGroupBox *groupBox,
		bool parsingVmt)
{
	if(checked) {
		groupBox->show();
		if (!parsingVmt)
			groupBox->fadeShow(mainWindow);
		groupBox->repaint();

	} else {
		groupBox->fadeHide(mainWindow);
	}
	mainWindow->refreshRequested();
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, TintSlider *sliderWidget,
		Ui::MainWindow *ui)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
		sliderWidget->initialize(colorWidget, color);
	}
}

void utils::applyBackgroundColor(const QString &parameter, const QString &value,
		QToolButton *colorWidget, Ui::MainWindow *ui, bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {
		int red = r.intValues.at(0);
		int green = r.intValues.at(1);
		int blue = r.intValues.at(2);
		utils::applyBackgroundColor(red, green, blue, colorWidget);

		QColor color;
		color.setRed(red);
		color.setGreen(green);
		color.setBlue(blue);
	}
}

void utils::applyColor(const QString &parameter, const QString &value,
	QToolButton *colorWidget, QDoubleSpinBox *spinBox, Ui::MainWindow *ui,
	bool toSrgb)
{
	utils::ColorResult r = utils::parseColor(parameter, value, ui, toSrgb);

	if (r.notDefault) {

		float red = r.doubleValues.at(0);
		float green = r.doubleValues.at(1);
		float blue = r.doubleValues.at(2);

		double max = static_cast<double>(qMax(red, qMax(green, blue)));

		red = red / max;
		green = green / max;
		blue = blue / max;

		utils::applyBackgroundColor(red * 255, green * 255, blue * 255,
			colorWidget);
		spinBox->setValue(max);
	}
}

void utils::applyBackgroundColor(int red, int green, int blue,
	QToolButton *colorWidget)
{
	// according to the documentation the cast to the string is not
	// required but we seem to need it
	QString s = QString("background-color: rgb(%1, %2, %3)")
		.arg(STR(red), STR(green), STR(blue));
	colorWi

}

QColor utils::getBG(QToolButton *widget)
{
	// TODO: Debug and explain how and why this works!
	QColor result;

	QString ss = widget->styleSheet();
	ss = ss.mid(ss.indexOf('(') + 1, ss.length() - ss.indexOf('(') - 2);

	QStringList list = ss.split(',');
	result.setRed(list.at(0).toInt());
	result.setGreen(list.at(1).toInt());
	result.setBlue(list.at(2).toInt());

	return result;
}

namespace utils {

QString getNonDef(QDoubleSpinBox *sb, const QString &def)
{
	if (!sb->isEnabled())
		return "";

	const QString text = STR(sb->value());
	if (stripZeroes(text) == def)
		return "";

	return text;
}

} // namespace utils


/******************************************************************************
BigWorld Technology
All Rights Reserved. Commercial in confidence.

treaties. Unauthorized use, reproduction or distribution of this program, or
any portion of this program, may result in the imposition of civil and
criminal penalties as provided by law.
******************************************************************************/

#include "column_type.hpp"

#include "helper_types.hpp"

#include <sstream>

/**
 *	This method converts the information in this ColumnType object into a
 * 	string suitable for a CREATE TABLE or ALTER TABLE statement.
 */
std::string ColumnType::getAsString( MySql & connection,
		ColumnIndexType idxType ) const
{
	// Builds up the type string using this object
	struct ResultStream
	{
		std::stringstream 	ss;
		bool				isUnsignedOrBinary;

		ResultStream( bool isUnsignedOrBinaryIn ) :
			isUnsignedOrBinary( isUnsignedOrBinaryIn )
		{}

{
			ss << typeName;
		}
		void addNumericalType( const char * typeName )
		{
			ss << typeName;
			if (isUnsignedOrBinary)
			{
				ss << " UNSIGNED";
			}
		}
		void addVarLenType( const char * typeName, uint length )
		{
			ss << typeName << '(' << length << ')';
		}
		void addVarLenStrType( uint length, const char * prefix = "" )
		{
			const char * charTypeStr = isUnsignedOrBinary ? "BINARY" : "CHAR";
			ss << prefix << charTypeStr << '(' << length << ')';
		}
		void addMultiLenType( const char * lenType )
		{
			const char * blobTypeStr = isUnsignedOrBinary ? "BLOB" : "TEXT";
			ss << lenType << blobTypeStr;
		}
		void addAutoIncrement()
		{
			ss << " AUTO_INCREMENT";
		}
		void addDefaultValue( const std::string& defaultValue,
				MySql* pConnection )
		{
			if (!defaultValue.empty())
			{
				ss << " DEFAULT ";
				if (pConnection)
				{
					ss << '\''
						<<	MySqlEscapedString( *pConnection, defaultValue )
						<< '\'';
				}
				else
				{
					ss << defaultValue;
				}
			}
		}
		void addPrimaryKey()
		{
			ss << " PRIMARY KEY";
		}
		void addNotNull()
		{
			ss << " NOT NULL";
		}
		void addOnUpdate( const std::string & cmd )
		{
			ss << " ON UPDATE " << cmd;
		}
	};
	ResultStream result( this->isUnsignedOrBinary );

	switch (fieldType)
	{
		case MYSQL_TYPE_TINY:
			result.addNumericalType( "TINYINT" );
			break;
		case MYSQL_TYPE_SHORT:
			result.addNumericalType( "SMALLINT" );
			break;
		case MYSQL_TYPE_INT24:
			result.addNumericalType( "MEDIUMINT" );
			break;
		case MYSQL_TYPE_LONG:
			result.addNumericalType( "INT" );
			break;
		case MYSQL_TYPE_LONGLONG:
			result.addNumericalType( "BIGINT" );
			break;
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			result.addNumericalType( "DECIMAL" );
			break;
		case MYSQL_TYPE_FLOAT:
			result.addType( "FLOAT" );
			break;
		case MYSQL_TYPE_DOUBLE:
			result.addType( "DOUBLE" );
			break;
		case MYSQL_TYPE_TIMESTAMP:
			result.addType( "TIMESTAMP" );
			break;
		case MYSQL_TYPE_TIME:
			result.addType( "TIME" );
			break;
		case MYSQL_TYPE_NEWDATE:
			result.addType( "DATE" );
			break;
		case MYSQL_TYPE_YEAR:
			result.addVarLenType( "YEAR", this->length );
			break;
		case MYSQL_TYPE_VARCHAR:
			result.addVarLenType( "VARCHAR", this->length );
			break;
		case MYSQL_TYPE_TINY_BLOB:
			result.addMultiLenType( "TINY" );
			break;
		case MYSQL_TYPE_MEDIUM_BLOB:
			result.addMultiLenType( "MEDIUM" );
			break;
		case MYSQL_TYPE_LONG_BLOB:
			result.addMultiLenType( "LONG" );
			break;
		case MYSQL_TYPE_BLOB:
			result.addMultiLenType( "" );
			break;
		case MYSQL_TYPE_BIT:
			result.addVarLenType( "BIT", this->length );
			break;
		case MYSQL_TYPE_VAR_STRING:
			result.addVarLenStrType( this->length, "VAR" );
			break;
		case MYSQL_TYPE_STRING:
			result.addVarLenStrType( this->length );
			break;
		default:
			MF_ASSERT( false );
			break;
	}

	if (this->isAutoIncrement)
	{
		result.addAutoIncrement();
	}
	else if (this->isDefaultValueSupported())
	{
		result.addDefaultValue( this->defaultValue,
				this->isStringType() ? &connection : NULL );
	}

	if (!this->onUpdateCmd.empty())
	{
		result.addOnUpdate( this->onUpdateCmd );
	}

	if (idxType == INDEX_TYPE_PRIMARY)
	{
		// The primary key has to be part of the column specification because
		// we want to create it when we create the table. If we do not, then
		// MySQL will create one automatically, and it may not be what we want.
		result.addPrimaryKey();
	}

	result.addNotNull();

	return result.ss.str();
}

/**
 * 	Returns true if the type supports the default value specification.
 */
std::string ColumnType::getDefaultValueAsString(
		MySql& connection ) const
{
	if (this->isStringType())
	{
		std::stringstream ss;
		ss << '\'' <<  MySqlEscapedString( connection, this->defaultValue )
				<< '\'';
		return ss.str();
	}
	else
	{
		return this->defaultValue;
	}
}

/**
 * 	Returns true if the type supports the default value specification.
 */
bool ColumnType::isDefaultValueSupported() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
			return false;
		default:
			return true;
	}
}

/**
 *	Whether the type is a string-like type.
 */
bool ColumnType::isStringType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
		case MYSQL_TYPE_VARCHAR:
			return true;
		default:
			return false;
	}
}

bool ColumnType::isSimpleNumericalType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
		case MYSQL_TYPE_FLOAT:
		case MYSQL_TYPE_DOUBLE:
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return true;
		default:
			return false;
	}
}

/**
 * 	Equality operator. Need to take into account that not all members are
 * 	relevant.
 */
bool ColumnType::operator==( const ColumnType & other ) const
{
	struct HelperFunctions
	{
		static bool equalOptionalStuff( const ColumnType & self, const ColumnType & other )
		{
			MF_ASSERT( self.fieldType == other.fieldType );

			switch (self.fieldType)
			{
				case MYSQL_TYPE_DECIMAL:
				case MYSQL_TYPE_NEWDECIMAL:
				case MYSQL_TYPE_FLOAT:
				case MYSQL_TYPE_DOUBLE:
				case MYSQL_TYPE_TIMESTAMP:
				case MYSQL_TYPE_TIME:
				case MYSQL_TYPE_NEWDATE:
					return true;
				case MYSQL_TYPE_TINY:
				case MYSQL_TYPE_SHORT:
				case MYSQL_TYPE_INT24:
				case MYSQL_TYPE_LONG:
				case MYSQL_TYPE_LONGLONG:
					return (self.isUnsigned() == other.isUnsigned())
							&& (self.isAutoIncrement == other.isAutoIncrement);
				case MYSQL_TYPE_TINY_BLOB:
				case MYSQL_TYPE_MEDIUM_BLOB:
				case MYSQL_TYPE_LONG_BLOB:
				case MYSQL_TYPE_BLOB:
					return self.isBinary() == other.isBinary();
				case MYSQL_TYPE_VAR_STRING:
				case MYSQL_TYPE_STRING:
					return (self.length == other.length) &&
						(self.isBinary() == other.isBinary());
				case MYSQL_TYPE_YEAR:
				case MYSQL_TYPE_VARCHAR:
					return self.length == other.length;
				default:
					MF_ASSERT( false );
					return true;
			}
		}

		static bool equalDefaultValue( const ColumnType & self, const ColumnType & other )
		{
			if (self.isAutoIncrement || !self.isDefaultValueSupported())
			{
				return true;
			}

			if (self.defaultValue == other.defaultValue)
			{
				return true;
			}

			if (self.isSimpleNumericalType())
			{
				return (self.defaultValue.empty() &&
						(other.defaultValue == "0")) ||
					((self.defaultValue == "0") && other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_STRING)
			{
				std::string nullString( self.length,
						self.isBinary() ? '\0' : ' ' );

				return (self.defaultValue.empty() &&
						(other.defaultValue == nullString)) ||
					((self.defaultValue == nullString) &&
						other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_TIMESTAMP)
			{
				return true;
			}

			return false;
		}
	};

	return (this->fieldType == other.fieldType) &&
			HelperFunctions::equalOptionalStuff( *this, other ) &&
			(this->isAutoIncrement == other.isAutoIncrement) &&
			HelperFunctions::equalDefaultValue( *this, other );
}

/**
 *	Returns true if the field is either an unsigned field or a binary blob
 * 	field.
 */
bool ColumnType::deriveIsUnsignedOrBinary( const MYSQL_FIELD & field )
{
	switch (field.type)
	{
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return (field.flags & UNSIGNED_FLAG);

		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
			// charsetnr is from mysql.h and represents the character set
			// of the associated MYSQL_FIELD. The magic number number 63
			// is supposed to represent 'CHARACTER SET binary'. This
			// however may be unsafe as the value comes from the file
			// mysql/charsets/Index.xml, which may be modified in different
			// versions of MySQL
			return (field.charsetnr == 63);

		default:
			// Doesn't apply to other types so we can return whatever we like.
			return false;
	}
}

// column_type.cpp


{
			ss << typeName;
			if (isUnsignedOrBinary)
			{
				ss << " UNSIGNED";
			}
		}

/******************************************************************************
BigWorld Technology
All Rights Reserved. Commercial in confidence.

treaties. Unauthorized use, reproduction or distribution of this program, or
any portion of this program, may result in the imposition of civil and
criminal penalties as provided by law.
******************************************************************************/

#include "column_type.hpp"

#include "helper_types.hpp"

#include <sstream>

/**
 *	This method converts the information in this ColumnType object into a
 * 	string suitable for a CREATE TABLE or ALTER TABLE statement.
 */
std::string ColumnType::getAsString( MySql & connection,
		ColumnIndexType idxType ) const
{
	// Builds up the type string using this object
	struct ResultStream
	{
		std::stringstream 	ss;
		bool				isUnsignedOrBinary;

		ResultStream( bool isUnsignedOrBinaryIn ) :
			isUnsignedOrBinary( isUnsignedOrBinaryIn )
		{}

		void addType( const char * typeName )
		{
			ss << typeName;
		}
		void addNumericalType( const char * typeName )

		void addVarLenType( const char * typeName, uint length )
		{
			ss << typeName << '(' << length << ')';
		}
		void addVarLenStrType( uint length, const char * prefix = "" )
		{
			const char * charTypeStr = isUnsignedOrBinary ? "BINARY" : "CHAR";
			ss << prefix << charTypeStr << '(' << length << ')';
		}
		void addMultiLenType( const char * lenType )
		{
			const char * blobTypeStr = isUnsignedOrBinary ? "BLOB" : "TEXT";
			ss << lenType << blobTypeStr;
		}
		void addAutoIncrement()
		{
			ss << " AUTO_INCREMENT";
		}
		void addDefaultValue( const std::string& defaultValue,
				MySql* pConnection )
		{
			if (!defaultValue.empty())
			{
				ss << " DEFAULT ";
				if (pConnection)
				{
					ss << '\''
						<<	MySqlEscapedString( *pConnection, defaultValue )
						<< '\'';
				}
				else
				{
					ss << defaultValue;
				}
			}
		}
		void addPrimaryKey()
		{
			ss << " PRIMARY KEY";
		}
		void addNotNull()
		{
			ss << " NOT NULL";
		}
		void addOnUpdate( const std::string & cmd )
		{
			ss << " ON UPDATE " << cmd;
		}
	};
	ResultStream result( this->isUnsignedOrBinary );

	switch (fieldType)
	{
		case MYSQL_TYPE_TINY:
			result.addNumericalType( "TINYINT" );
			break;
		case MYSQL_TYPE_SHORT:
			result.addNumericalType( "SMALLINT" );
			break;
		case MYSQL_TYPE_INT24:
			result.addNumericalType( "MEDIUMINT" );
			break;
		case MYSQL_TYPE_LONG:
			result.addNumericalType( "INT" );
			break;
		case MYSQL_TYPE_LONGLONG:
			result.addNumericalType( "BIGINT" );
			break;
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			result.addNumericalType( "DECIMAL" );
			break;
		case MYSQL_TYPE_FLOAT:
			result.addType( "FLOAT" );
			break;
		case MYSQL_TYPE_DOUBLE:
			result.addType( "DOUBLE" );
			break;
		case MYSQL_TYPE_TIMESTAMP:
			result.addType( "TIMESTAMP" );
			break;
		case MYSQL_TYPE_TIME:
			result.addType( "TIME" );
			break;
		case MYSQL_TYPE_NEWDATE:
			result.addType( "DATE" );
			break;
		case MYSQL_TYPE_YEAR:
			result.addVarLenType( "YEAR", this->length );
			break;
		case MYSQL_TYPE_VARCHAR:
			result.addVarLenType( "VARCHAR", this->length );
			break;
		case MYSQL_TYPE_TINY_BLOB:
			result.addMultiLenType( "TINY" );
			break;
		case MYSQL_TYPE_MEDIUM_BLOB:
			result.addMultiLenType( "MEDIUM" );
			break;
		case MYSQL_TYPE_LONG_BLOB:
			result.addMultiLenType( "LONG" );
			break;
		case MYSQL_TYPE_BLOB:
			result.addMultiLenType( "" );
			break;
		case MYSQL_TYPE_BIT:
			result.addVarLenType( "BIT", this->length );
			break;
		case MYSQL_TYPE_VAR_STRING:
			result.addVarLenStrType( this->length, "VAR" );
			break;
		case MYSQL_TYPE_STRING:
			result.addVarLenStrType( this->length );
			break;
		default:
			MF_ASSERT( false );
			break;
	}

	if (this->isAutoIncrement)
	{
		result.addAutoIncrement();
	}
	else if (this->isDefaultValueSupported())
	{
		result.addDefaultValue( this->defaultValue,
				this->isStringType() ? &connection : NULL );
	}

	if (!this->onUpdateCmd.empty())
	{
		result.addOnUpdate( this->onUpdateCmd );
	}

	if (idxType == INDEX_TYPE_PRIMARY)
	{
		// The primary key has to be part of the column specification because
		// we want to create it when we create the table. If we do not, then
		// MySQL will create one automatically, and it may not be what we want.
		result.addPrimaryKey();
	}

	result.addNotNull();

	return result.ss.str();
}

/**
 * 	Returns true if the type supports the default value specification.
 */
std::string ColumnType::getDefaultValueAsString(
		MySql& connection ) const
{
	if (this->isStringType())
	{
		std::stringstream ss;
		ss << '\'' <<  MySqlEscapedString( connection, this->defaultValue )
				<< '\'';
		return ss.str();
	}
	else
	{
		return this->defaultValue;
	}
}

/**
 * 	Returns true if the type supports the default value specification.
 */
bool ColumnType::isDefaultValueSupported() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
			return false;
		default:
			return true;
	}
}

/**
 *	Whether the type is a string-like type.
 */
bool ColumnType::isStringType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
		case MYSQL_TYPE_VARCHAR:
			return true;
		default:
			return false;
	}
}

bool ColumnType::isSimpleNumericalType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
		case MYSQL_TYPE_FLOAT:
		case MYSQL_TYPE_DOUBLE:
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return true;
		default:
			return false;
	}
}

/**
 * 	Equality operator. Need to take into account that not all members are
 * 	relevant.
 */
bool ColumnType::operator==( const ColumnType & other ) const
{
	struct HelperFunctions
	{
		static bool equalOptionalStuff( const ColumnType & self, const ColumnType & other )
		{
			MF_ASSERT( self.fieldType == other.fieldType );

			switch (self.fieldType)
			{
				case MYSQL_TYPE_DECIMAL:
				case MYSQL_TYPE_NEWDECIMAL:
				case MYSQL_TYPE_FLOAT:
				case MYSQL_TYPE_DOUBLE:
				case MYSQL_TYPE_TIMESTAMP:
				case MYSQL_TYPE_TIME:
				case MYSQL_TYPE_NEWDATE:
					return true;
				case MYSQL_TYPE_TINY:
				case MYSQL_TYPE_SHORT:
				case MYSQL_TYPE_INT24:
				case MYSQL_TYPE_LONG:
				case MYSQL_TYPE_LONGLONG:
					return (self.isUnsigned() == other.isUnsigned())
							&& (self.isAutoIncrement == other.isAutoIncrement);
				case MYSQL_TYPE_TINY_BLOB:
				case MYSQL_TYPE_MEDIUM_BLOB:
				case MYSQL_TYPE_LONG_BLOB:
				case MYSQL_TYPE_BLOB:
					return self.isBinary() == other.isBinary();
				case MYSQL_TYPE_VAR_STRING:
				case MYSQL_TYPE_STRING:
					return (self.length == other.length) &&
						(self.isBinary() == other.isBinary());
				case MYSQL_TYPE_YEAR:
				case MYSQL_TYPE_VARCHAR:
					return self.length == other.length;
				default:
					MF_ASSERT( false );
					return true;
			}
		}

		static bool equalDefaultValue( const ColumnType & self, const ColumnType & other )
		{
			if (self.isAutoIncrement || !self.isDefaultValueSupported())
			{
				return true;
			}

			if (self.defaultValue == other.defaultValue)
			{
				return true;
			}

			if (self.isSimpleNumericalType())
			{
				return (self.defaultValue.empty() &&
						(other.defaultValue == "0")) ||
					((self.defaultValue == "0") && other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_STRING)
			{
				std::string nullString( self.length,
						self.isBinary() ? '\0' : ' ' );

				return (self.defaultValue.empty() &&
						(other.defaultValue == nullString)) ||
					((self.defaultValue == nullString) &&
						other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_TIMESTAMP)
			{
				return true;
			}

			return false;
		}
	};

	return (this->fieldType == other.fieldType) &&
			HelperFunctions::equalOptionalStuff( *this, other ) &&
			(this->isAutoIncrement == other.isAutoIncrement) &&
			HelperFunctions::equalDefaultValue( *this, other );
}

/**
 *	Returns true if the field is either an unsigned field or a binary blob
 * 	field.
 */
bool ColumnType::deriveIsUnsignedOrBinary( const MYSQL_FIELD & field )
{
	switch (field.type)
	{
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return (field.flags & UNSIGNED_FLAG);

		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
			// charsetnr is from mysql.h and represents the character set
			// of the associated MYSQL_FIELD. The magic number number 63
			// is supposed to represent 'CHARACTER SET binary'. This
			// however may be unsafe as the value comes from the file
			// mysql/charsets/Index.xml, which may be modified in different
			// versions of MySQL
			return (field.charsetnr == 63);

		default:
			// Doesn't apply to other types so we can return whatever we like.
			return false;
	}
}

// column_type.cpp


/******************************************************************************
BigWorld Technology
All Rights Reserved. Commercial in confidence.

treaties. Unauthorized use, reproduction or distribution of this program, or
any portion of this program, may result in the imposition of civil and
criminal penalties as provided by law.
******************************************************************************/

#include "column_type.hpp"

#include "helper_types.hpp"

#include <sstream>

/**
 *	This method converts the information in this ColumnType object into a
 * 	string suitable for a CREATE TABLE or ALTER TABLE statement.
 */
std::string ColumnType::getAsString( MySql & connection,
		ColumnIndexType idxType ) const
{
	// Builds up the type string using this object
	struct ResultStream
	{
		std::stringstream 	ss;
		bool				isUnsignedOrBinary;

		ResultStream( bool isUnsignedOrBinaryIn ) :
			isUnsignedOrBinary( isUnsignedOrBinaryIn )
		{}

		void addType( const char * typeName )
		{
			ss << typeName;
		}
		void addNumericalType( const char * typeName )
		{
			ss << typeName;
			if (isUnsignedOrBinary)
			{
				ss << " UNSIGNED";
			}
		}
		void addVarLenType( const char * typeName, uint length )
		{
			ss << typeName << '(' << length << ')';
		}
		void addVarLenStrType( uint length, const char * prefix = "" )
		{
			const char * charTypeStr = isUnsignedOrBinary ? "BINARY" : "CHAR";
			ss << prefix << charTypeStr << '(' << length << ')';
		}
		void addMultiLenType( const char * lenType )
		{
			const char * blobTypeStr = isUnsignedOrBinary ? "BLOB" : "TEXT";
			ss << lenType << blo

		}
		void addAutoIncrement()
		{
			ss << " AUTO_INCREMENT";
		}
		void addDefaultValue( const std::string& defaultValue,
				MySql* pConnection )
		{
			if (!defaultValue.empty())
			{
				ss << " DEFAULT ";
				if (pConnection)
				{
					ss << '\''
						<<	MySqlEscapedString( *pConnection, defaultValue )
						<< '\'';
				}
				else
				{
					ss << defaultValue;
				}
			}
		}
		void addPrimaryKey()
		{
			ss << " PRIMARY KEY";
		}
		void addNotNull()
		{
			ss << " NOT NULL";
		}
		void addOnUpdate( const std::string & cmd )
		{
			ss << " ON UPDATE " << cmd;
		}
	};
	ResultStream result( this->isUnsignedOrBinary );

	switch (fieldType)
	{
		case MYSQL_TYPE_TINY:
			result.addNumericalType( "TINYINT" );
			break;
		case MYSQL_TYPE_SHORT:
			result.addNumericalType( "SMALLINT" );
			break;
		case MYSQL_TYPE_INT24:
			result.addNumericalType( "MEDIUMINT" );
			break;
		case MYSQL_TYPE_LONG:
			result.addNumericalType( "INT" );
			break;
		case MYSQL_TYPE_LONGLONG:
			result.addNumericalType( "BIGINT" );
			break;
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			result.addNumericalType( "DECIMAL" );
			break;
		case MYSQL_TYPE_FLOAT:
			result.addType( "FLOAT" );
			break;
		case MYSQL_TYPE_DOUBLE:
			result.addType( "DOUBLE" );
			break;
		case MYSQL_TYPE_TIMESTAMP:
			result.addType( "TIMESTAMP" );
			break;
		case MYSQL_TYPE_TIME:
			result.addType( "TIME" );
			break;
		case MYSQL_TYPE_NEWDATE:
			result.addType( "DATE" );
			break;
		case MYSQL_TYPE_YEAR:
			result.addVarLenType( "YEAR", this->length );
			break;
		case MYSQL_TYPE_VARCHAR:
			result.addVarLenType( "VARCHAR", this->length );
			break;
		case MYSQL_TYPE_TINY_BLOB:
			result.addMultiLenType( "TINY" );
			break;
		case MYSQL_TYPE_MEDIUM_BLOB:
			result.addMultiLenType( "MEDIUM" );
			break;
		case MYSQL_TYPE_LONG_BLOB:
			result.addMultiLenType( "LONG" );
			break;
		case MYSQL_TYPE_BLOB:
			result.addMultiLenType( "" );
			break;
		case MYSQL_TYPE_BIT:
			result.addVarLenType( "BIT", this->length );
			break;
		case MYSQL_TYPE_VAR_STRING:
			result.addVarLenStrType( this->length, "VAR" );
			break;
		case MYSQL_TYPE_STRING:
			result.addVarLenStrType( this->length );
			break;
		default:
			MF_ASSERT( false );
			break;
	}

	if (this->isAutoIncrement)
	{
		result.addAutoIncrement();
	}
	else if (this->isDefaultValueSupported())
	{
		result.addDefaultValue( this->defaultValue,
				this->isStringType() ? &connection : NULL );
	}

	if (!this->onUpdateCmd.empty())
	{
		result.addOnUpdate( this->onUpdateCmd );
	}

	if (idxType == INDEX_TYPE_PRIMARY)
	{
		// The primary key has to be part of the column specification because
		// we want to create it when we create the table. If we do not, then
		// MySQL will create one automatically, and it may not be what we want.
		result.addPrimaryKey();
	}

	result.addNotNull();

	return result.ss.str();
}

/**
 * 	Returns true if the type supports the default value specification.
 */
std::string ColumnType::getDefaultValueAsString(
		MySql& connection ) const
{
	if (this->isStringType())
	{
		std::stringstream ss;
		ss << '\'' <<  MySqlEscapedString( connection, this->defaultValue )
				<< '\'';
		return ss.str();
	}
	else
	{
		return this->defaultValue;
	}
}

/**
 * 	Returns true if the type supports the default value specification.
 */
bool ColumnType::isDefaultValueSupported() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
			return false;
		default:
			return true;
	}
}

/**
 *	Whether the type is a string-like type.
 */
bool ColumnType::isStringType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
		case MYSQL_TYPE_VARCHAR:
			return true;
		default:
			return false;
	}
}

bool ColumnType::isSimpleNumericalType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
		case MYSQL_TYPE_FLOAT:
		case MYSQL_TYPE_DOUBLE:
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return true;
		default:
			return false;
	}
}

/**
 * 	Equality operator. Need to take into account that not all members are
 * 	relevant.
 */
bool ColumnType::operator==( const ColumnType & other ) const
{
	struct HelperFunctions
	{
		static bool equalOptionalStuff( const ColumnType & self, const ColumnType & other )
		{
			MF_ASSERT( self.fieldType == other.fieldType );

			switch (self.fieldType)
			{
				case MYSQL_TYPE_DECIMAL:
				case MYSQL_TYPE_NEWDECIMAL:
				case MYSQL_TYPE_FLOAT:
				case MYSQL_TYPE_DOUBLE:
				case MYSQL_TYPE_TIMESTAMP:
				case MYSQL_TYPE_TIME:
				case MYSQL_TYPE_NEWDATE:
					return true;
				case MYSQL_TYPE_TINY:
				case MYSQL_TYPE_SHORT:
				case MYSQL_TYPE_INT24:
				case MYSQL_TYPE_LONG:
				case MYSQL_TYPE_LONGLONG:
					return (self.isUnsigned() == other.isUnsigned())
							&& (self.isAutoIncrement == other.isAutoIncrement);
				case MYSQL_TYPE_TINY_BLOB:
				case MYSQL_TYPE_MEDIUM_BLOB:
				case MYSQL_TYPE_LONG_BLOB:
				case MYSQL_TYPE_BLOB:
					return self.isBinary() == other.isBinary();
				case MYSQL_TYPE_VAR_STRING:
				case MYSQL_TYPE_STRING:
					return (self.length == other.length) &&
						(self.isBinary() == other.isBinary());
				case MYSQL_TYPE_YEAR:
				case MYSQL_TYPE_VARCHAR:
					return self.length == other.length;
				default:
					MF_ASSERT( false );
					return true;
			}
		}

		static bool equalDefaultValue( const ColumnType & self, const ColumnType & other )
		{
			if (self.isAutoIncrement || !self.isDefaultValueSupported())
			{
				return true;
			}

			if (self.defaultValue == other.defaultValue)
			{
				return true;
			}

			if (self.isSimpleNumericalType())
			{
				return (self.defaultValue.empty() &&
						(other.defaultValue == "0")) ||
					((self.defaultValue == "0") && other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_STRING)
			{
				std::string nullString( self.length,
						self.isBinary() ? '\0' : ' ' );

				return (self.defaultValue.empty() &&
						(other.defaultValue == nullString)) ||
					((self.defaultValue == nullString) &&
						other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_TIMESTAMP)
			{
				return true;
			}

			return false;
		}
	};

	return (this->fieldType == other.fieldType) &&
			HelperFunctions::equalOptionalStuff( *this, other ) &&
			(this->isAutoIncrement == other.isAutoIncrement) &&
			HelperFunctions::equalDefaultValue( *this, other );
}

/**
 *	Returns true if the field is either an unsigned field or a binary blob
 * 	field.
 */
bool ColumnType::deriveIsUnsignedOrBinary( const MYSQL_FIELD & field )
{
	switch (field.type)
	{
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return (field.flags & UNSIGNED_FLAG);

		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
			// charsetnr is from mysql.h and represents the character set
			// of the associated MYSQL_FIELD. The magic number number 63
			// is supposed to represent 'CHARACTER SET binary'. This
			// however may be unsafe as the value comes from the file
			// mysql/charsets/Index.xml, which may be modified in different
			// versions of MySQL
			return (field.charsetnr == 63);

		default:
			// Doesn't apply to other types so we can return whatever we like.
			return false;
	}
}

// column_type.cpp


bool ColumnType::isDefaultValueSupported() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
			return false;
		default:
			return true;
	}
}

/******************************************************************************
BigWorld Technology
All Rights Reserved. Commercial in confidence.

treaties. Unauthorized use, reproduction or distribution of this program, or
any portion of this program, may result in the imposition of civil and
criminal penalties as provided by law.
******************************************************************************/

#include "column_type.hpp"

#include "helper_types.hpp"

#include <sstream>

/**
 *	This method converts the information in this ColumnType object into a
 * 	string suitable for a CREATE TABLE or ALTER TABLE statement.
 */
std::string ColumnType::getAsString( MySql & connection,
		ColumnIndexType idxType ) const
{
	// Builds up the type string using this object
	struct ResultStream
	{
		std::stringstream 	ss;
		bool				isUnsignedOrBinary;

		ResultStream( bool isUnsignedOrBinaryIn ) :
			isUnsignedOrBinary( isUnsignedOrBinaryIn )
		{}

		void addType( const char * typeName )
		{
			ss << typeName;
		}
		void addNumericalType( const char * typeName )
		{
			ss << typeName;
			if (isUnsignedOrBinary)
			{
				ss << " UNSIGNED";
			}
		}
		void addVarLenType( const char * typeName, uint length )
		{
			ss << typeName << '(' << length << ')';
		}
		void addVarLenStrType( uint length, const char * prefix = "" )
		{
			const char * charTypeStr = isUnsignedOrBinary ? "BINARY" : "CHAR";
			ss << prefix << charTypeStr << '(' << length << ')';
		}
		void addMultiLenType( const char * lenType )
		{
			const char * blobTypeStr = isUnsignedOrBinary ? "BLOB" : "TEXT";
			ss << lenType << blobTypeStr;
		}
		void addAutoIncrement()
		{
			ss << " AUTO_INCREMENT";
		}
		void addDefaultValue( const std::string& defaultValue,
				MySql* pConnection )
		{
			if (!defaultValue.empty())
			{
				ss << " DEFAULT ";
				if (pConnection)
				{
					ss << '\''
						<<	MySqlEscapedString( *pConnection, defaultValue )
						<< '\'';
				}
				else
				{
					ss << defaultValue;
				}
			}
		}
		void addPrimaryKey()
		{
			ss << " PRIMARY KEY";
		}
		void addNotNull()
		{
			ss << " NOT NULL";
		}
		void addOnUpdate( const std::string & cmd )
		{
			ss << " ON UPDATE " << cmd;
		}
	};
	ResultStream result( this->isUnsignedOrBinary );

	switch (fieldType)
	{
		case MYSQL_TYPE_TINY:
			result.addNumericalType( "TINYINT" );
			break;
		case MYSQL_TYPE_SHORT:
			result.addNumericalType( "SMALLINT" );
			break;
		case MYSQL_TYPE_INT24:
			result.addNumericalType( "MEDIUMINT" );
			break;
		case MYSQL_TYPE_LONG:
			result.addNumericalType( "INT" );
			break;
		case MYSQL_TYPE_LONGLONG:
			result.addNumericalType( "BIGINT" );
			break;
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
			result.addNumericalType( "DECIMAL" );
			break;
		case MYSQL_TYPE_FLOAT:
			result.addType( "FLOAT" );
			break;
		case MYSQL_TYPE_DOUBLE:
			result.addType( "DOUBLE" );
			break;
		case MYSQL_TYPE_TIMESTAMP:
			result.addType( "TIMESTAMP" );
			break;
		case MYSQL_TYPE_TIME:
			result.addType( "TIME" );
			break;
		case MYSQL_TYPE_NEWDATE:
			result.addType( "DATE" );
			break;
		case MYSQL_TYPE_YEAR:
			result.addVarLenType( "YEAR", this->length );
			break;
		case MYSQL_TYPE_VARCHAR:
			result.addVarLenType( "VARCHAR", this->length );
			break;
		case MYSQL_TYPE_TINY_BLOB:
			result.addMultiLenType( "TINY" );
			break;
		case MYSQL_TYPE_MEDIUM_BLOB:
			result.addMultiLenType( "MEDIUM" );
			break;
		case MYSQL_TYPE_LONG_BLOB:
			result.addMultiLenType( "LONG" );
			break;
		case MYSQL_TYPE_BLOB:
			result.addMultiLenType( "" );
			break;
		case MYSQL_TYPE_BIT:
			result.addVarLenType( "BIT", this->length );
			break;
		case MYSQL_TYPE_VAR_STRING:
			result.addVarLenStrType( this->length, "VAR" );
			break;
		case MYSQL_TYPE_STRING:
			result.addVarLenStrType( this->length );
			break;
		default:
			MF_ASSERT( false );
			break;
	}

	if (this->isAutoIncrement)
	{
		result.addAutoIncrement();
	}
	else if (this->isDefaultValueSupported())
	{
		result.addDefaultValue( this->defaultValue,
				this->isStringType() ? &connection : NULL );
	}

	if (!this->onUpdateCmd.empty())
	{
		result.addOnUpdate( this->onUpdateCmd );
	}

	if (idxType == INDEX_TYPE_PRIMARY)
	{
		// The primary key has to be part of the column specification because
		// we want to create it when we create the table. If we do not, then
		// MySQL will create one automatically, and it may not be what we want.
		result.addPrimaryKey();
	}

	result.addNotNull();

	return result.ss.str();
}

/**
 * 	Returns true if the type supports the default value specification.
 */
std::string ColumnType::getDefaultValueAsString(
		MySql& connection ) const
{
	if (this->isStringType())
	{
		std::stringstream ss;
		ss << '\'' <<  MySqlEscapedString( connection, this->defaultValue )
				<< '\'';
		return ss.str();
	}
	else
	{
		return this->defaultValue;
	}
}

/**
 * 	Returns true if the type supports the default value specification.
 */

/**
 *	Whether the type is a string-like type.
 */
bool ColumnType::isStringType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
		case MYSQL_TYPE_VARCHAR:
			return true;
		default:
			return false;
	}
}

bool ColumnType::isSimpleNumericalType() const
{
	switch (this->fieldType)
	{
		case MYSQL_TYPE_DECIMAL:
		case MYSQL_TYPE_NEWDECIMAL:
		case MYSQL_TYPE_FLOAT:
		case MYSQL_TYPE_DOUBLE:
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return true;
		default:
			return false;
	}
}

/**
 * 	Equality operator. Need to take into account that not all members are
 * 	relevant.
 */
bool ColumnType::operator==( const ColumnType & other ) const
{
	struct HelperFunctions
	{
		static bool equalOptionalStuff( const ColumnType & self, const ColumnType & other )
		{
			MF_ASSERT( self.fieldType == other.fieldType );

			switch (self.fieldType)
			{
				case MYSQL_TYPE_DECIMAL:
				case MYSQL_TYPE_NEWDECIMAL:
				case MYSQL_TYPE_FLOAT:
				case MYSQL_TYPE_DOUBLE:
				case MYSQL_TYPE_TIMESTAMP:
				case MYSQL_TYPE_TIME:
				case MYSQL_TYPE_NEWDATE:
					return true;
				case MYSQL_TYPE_TINY:
				case MYSQL_TYPE_SHORT:
				case MYSQL_TYPE_INT24:
				case MYSQL_TYPE_LONG:
				case MYSQL_TYPE_LONGLONG:
					return (self.isUnsigned() == other.isUnsigned())
							&& (self.isAutoIncrement == other.isAutoIncrement);
				case MYSQL_TYPE_TINY_BLOB:
				case MYSQL_TYPE_MEDIUM_BLOB:
				case MYSQL_TYPE_LONG_BLOB:
				case MYSQL_TYPE_BLOB:
					return self.isBinary() == other.isBinary();
				case MYSQL_TYPE_VAR_STRING:
				case MYSQL_TYPE_STRING:
					return (self.length == other.length) &&
						(self.isBinary() == other.isBinary());
				case MYSQL_TYPE_YEAR:
				case MYSQL_TYPE_VARCHAR:
					return self.length == other.length;
				default:
					MF_ASSERT( false );
					return true;
			}
		}

		static bool equalDefaultValue( const ColumnType & self, const ColumnType & other )
		{
			if (self.isAutoIncrement || !self.isDefaultValueSupported())
			{
				return true;
			}

			if (self.defaultValue == other.defaultValue)
			{
				return true;
			}

			if (self.isSimpleNumericalType())
			{
				return (self.defaultValue.empty() &&
						(other.defaultValue == "0")) ||
					((self.defaultValue == "0") && other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_STRING)
			{
				std::string nullString( self.length,
						self.isBinary() ? '\0' : ' ' );

				return (self.defaultValue.empty() &&
						(other.defaultValue == nullString)) ||
					((self.defaultValue == nullString) &&
						other.defaultValue.empty());
			}

			if (self.fieldType == MYSQL_TYPE_TIMESTAMP)
			{
				return true;
			}

			return false;
		}
	};

	return (this->fieldType == other.fieldType) &&
			HelperFunctions::equalOptionalStuff( *this, other ) &&
			(this->isAutoIncrement == other.isAutoIncrement) &&
			HelperFunctions::equalDefaultValue( *this, other );
}

/**
 *	Returns true if the field is either an unsigned field or a binary blob
 * 	field.
 */
bool ColumnType::deriveIsUnsignedOrBinary( const MYSQL_FIELD & field )
{
	switch (field.type)
	{
		case MYSQL_TYPE_TINY:
		case MYSQL_TYPE_SHORT:
		case MYSQL_TYPE_INT24:
		case MYSQL_TYPE_LONG:
		case MYSQL_TYPE_LONGLONG:
			return (field.flags & UNSIGNED_FLAG);

		case MYSQL_TYPE_TINY_BLOB:
		case MYSQL_TYPE_MEDIUM_BLOB:
		case MYSQL_TYPE_LONG_BLOB:
		case MYSQL_TYPE_BLOB:
		case MYSQL_TYPE_VAR_STRING:
		case MYSQL_TYPE_STRING:
			// charsetnr is from mysql.h and represents the character set
			// of the associated MYSQL_FIELD. The magic number number 63
			// is supposed to represent 'CHARACTER SET binary'. This
			// however may be unsafe as the value comes from the file
			// mysql/charsets/Index.xml, which may be modified in different
			// versions of MySQL
			return (field.charsetnr == 63);

		default:
			// Doesn't apply to other types so we can return whatever we like.
			return false;
	}
}

// column_type.cpp