/********************************************************************
Vireio Perception: Open-Source Stereoscopic 3D Driver
Copyright (C) 2012 Andres Hernandez

File <Main.cpp> :
Copyright (C) 2012 Andres Hernandez

Vireio Perception Version History:
v1.0.0 2012 by Andres Hernandez
v1.0.X 2013 by John Hicks, Neil Schneider
v1.1.x 2013 by Primary Coding Author: Chris Drain
Team Support: John Hicks, Phil Larkson, Neil Schneider
v2.0.x 2013 by Denis Reischl, Neil Schneider, Joshua Brown

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
********************************************************************/

// define this for Version 3 compile ----->>>>>> #define _VIREIO_3

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <Shlwapi.h>
#include <urlmon.h>
#include <string>
#include "hijackdll.h"
#include <CommCtrl.h> 
#include <ctime>  
#include <cstdlib>  
#include "ProxyHelper.h"
#include <vector>
#include "Resource.h"
#include "Version.h"
#include "..\PluginSection\Include\Vireio_GUI.h"
#include "loadjpg.h"
#pragma comment(lib, "Urlmon.lib")
#pragma comment(lib, "Shlwapi.lib")
#ifndef _VIREIO_3
#include "pugixml.hpp"
using namespace pugi;
#endif

#define APP_SIZE_WIDTH 800
#define APP_SIZE_HEIGHT 360
#ifdef _VIREIO_3
#define APP_COLOR_FONT RGB(10, 140, 100)
#define APP_COLOR_BACK RGB(10, 30, 40)
#define APP_SIZE_FONT 32
#else
#define APP_COLOR_FONT RGB(100, 40, 40)
#define APP_COLOR_BACK RGB(30, 5, 5)
#define APP_SIZE_FONT 22
#endif
#define APP_BUTTON_PENCOLOR RGB(0, 0, 0)
#define APP_BUTTON_PENCOLOR_INIT RGB(0, 0, 0)
#define APP_BUTTON_PENCOLOR_IDLE RGB(128, 128, 128)
#define APP_BUTTON_PENCOLOR_INJECTING RGB(255, 255, 0)
#define APP_BUTTON_PENCOLOR_TOINJECT RGB(128, 128, 0)
#define APP_BUTTON_PENCOLOR_INJECTED RGB(0, 255, 0)
#define APP_BUTTON_PENCOLOR_CLOSED RGB(255, 0, 0)
#define APP_BUTTON_BRUSHCOLOR RGB(80, 60, 60)
#define APP_FONT L"Constantia"
#define APP_FONT_ITALIC FALSE

#define DEBUG_UINT_A(a) { char buf[128]; wsprintf(buf, "%u", a); OutputDebugString(buf); }
#define DEBUG_HEX_W(a) { wchar_t buf[128]; wsprintfW(buf, L"%x", a); OutputDebugStringW(buf); }

using namespace std;

#ifndef _VIREIO_3
/*** Aquilinus Initialization method ***/
bool LoadAquilinusRTE();

/*** Aquilinus Runtime Environment type definitions ***/
typedef void(*AQUILINUS_Init)();
typedef void(*AQUILINUS_Close)();
typedef void(*AQUILINUS_ForceIdle)();
typedef void(*AQUILINUS_LoadProfile)(LPCWSTR szPath, LPCWSTR szProcess, LPCWSTR* szProcReturn, DWORD unRepetition, DWORD unTimedelay);
typedef void(*AQUILINUS_Reinject)();
typedef int(*AQUILINUS_GetInjectionState)();

/*** Aquilinus Runtime Environment methods ***/
AQUILINUS_Init g_pAquilinus_Init;
AQUILINUS_Close g_pAquilinus_Close;
AQUILINUS_ForceIdle g_pAquilinus_ForceIdle;
AQUILINUS_LoadProfile g_pAquilinus_LoadProfile;
AQUILINUS_Reinject g_pAquilinus_Reinject;
AQUILINUS_GetInjectionState g_pAquilinus_GetInjectionState;

/**
* Handle to the Aquilinus Runtime Environment.
***/
HMODULE g_hmAquilinusRTE = NULL;
/**
* True if Aquilinus profile to be loaded.
***/
bool g_bLoadAquilinusProfile = false;
/**
* Aquilinus injection state.
***/
enum InjectionState
{
	Initial = 0,
	Idle = 1,
	Injecting = 2,
	ToInject = 3,
	Injected = 4,
	Closed = 5
} g_eInjectionState;
/**
* Game profiles structure.
* Contains any information about a game process.
***/
struct VireioGameProfile
{
	enum DX_Version
	{
		DX_Version_9,
		DX_Version_10,
		DX_Version_11,
	} eDX_Version;
	enum CPU_Architecture
	{
		CPU_Architecture_32bit,
		CPU_Architecture_64bit
	} eCPU_Architecture;
	std::string szGameName;
	std::string szGameProcess;
	std::string szProfileFilePath;
	std::string szGameImageUrl;
	DWORD unTimedelay;
	DWORD unRepetition;
};
/**
* Vector containing all mono cinema profiles.
***/
std::vector<VireioGameProfile> m_asVireioGameProfiles;

/*** Vireio v3 config registry path method ***/
bool InitConfig(void);

#else
/*** Vireio v3 methods and fields ***/
bool InitConfig(void);
bool InitModes(void);
void LoadDLL(void);
HINSTANCE hD3D9;
ProxyHelper::OculusProfile oculusProfile;

/**
* Vector of Vireio Perception Stereo View Render Modes.
***/
vector<int> anStereoModes;
/**
* Vector of Vireio Perception Head Tracker Modes.
***/
vector<int> anTrackerModes;
/**
* Vector of installed Monitors.
***/
vector<int> anMonitors;
#endif
/*** Vireio methods ***/
std::string getCurrentPath(void);


/**
* Simple helper
***/
string StringToLower(const string& str)
{
	string result = str;
	for (unsigned ii = 0; ii < result.size(); ii++)
		result[ii] = ::tolower(result[ii]);
	return result;
}

/**
* Vireio Perception Window Class
***/
class Vireio_Perception_Main_Window
{
private:
	LPCSTR    window_class_name;
	HINSTANCE instance_handle;
	HCURSOR   cursor_arrow;
	HWND      window_handle;
	HWND      header_handle;
	RECT      client_rectangle;
	RECT      extended_profile_rectangle;
	static HBITMAP     logo_bitmap;
	// static HBITMAP     game_bitmap;
	static HBITMAP     button_bitmap[6];
	static POINT       m_ptMouseCursor;            /**< The current mouse cursor. **/
	static Vireio_GUI* m_pcVireioGUI;              /**< Vireio Graphical User Interface class. **/
	static UINT        m_dwListGameProfiles;       /**< Game profiles selection. ***/
	static UINT        m_dwSpinHMD;                /**< HMD selection. ***/
	static UINT        m_dwSpinStereoView;         /**< Main driver stereo view selection. ***/
	static UINT        m_dwSpinTracker;            /**< Main driver tracker selection. ***/
	static UINT        m_dwSpinMonitor;            /**< Main driver monitor selection. ***/
	static UINT        m_dwLoadAquilinusProfile;   /**< The Load Profile Button. (184x69 - same as 'Steam' thumbnails)  ***/
	static UINT        m_dwMinimize;               /**< Minimize button. ***/
	static UINT        m_dwExit;                   /**< Exit button. ***/
public:
	/**
	* Window Constructor.
	***/
	Vireio_Perception_Main_Window(LPCSTR window_class_identity) :
		window_class_name(window_class_identity)
	{
		// get screen and module handle
		int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
		int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
		instance_handle = GetModuleHandle(NULL);

		// register window class
		WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,
			instance_handle, NULL,
			NULL, NULL, NULL,
			window_class_name };

		window_class.hIcon = LoadIcon(instance_handle, MAKEINTRESOURCE(IDI_ICON_BIG));
		RegisterClass(&window_class);

		// create the window
		window_handle = CreateWindowEx(WS_EX_COMPOSITED,
			window_class_name,
			"Vireio Perception",
			WS_POPUP | WS_BORDER,
			(screen_width - 585) / 2, (screen_height - 240) / 2,
			APP_SIZE_WIDTH, APP_SIZE_HEIGHT,
			NULL, NULL, instance_handle, NULL);

		GetClientRect(window_handle, &client_rectangle);
		int width = client_rectangle.right - client_rectangle.left;
		int height = client_rectangle.bottom - client_rectangle.top;

#ifdef _VIREIO_3
		// load proxy config
		ProxyHelper helper = ProxyHelper();
		ProxyConfig cfg;
		helper.LoadUserConfig(cfg, oculusProfile);
#else
		// get global config
#ifdef _WIN64
		string configPath = string("..\\cfg_v4\\config_64bit.xml");
#else
		string configPath = string("..\\cfg_v4\\config_32bit.xml");
#endif
		xml_document docConfig;
		xml_parse_result resultConfig = docConfig.load_file(configPath.c_str());
		UINT unProfileIndex = 0;
		UINT unHMDIndex = 0;

		if (resultConfig.status == status_ok)
		{
			xml_node xml_config = docConfig.child("config");

			unProfileIndex = xml_config.attribute("profile_index").as_uint(0);
			unHMDIndex = xml_config.attribute("hmd_index").as_uint(0);
		}
#endif

		// create the vireio gui
		SIZE sSizeOfThis;
		sSizeOfThis.cx = APP_SIZE_WIDTH; sSizeOfThis.cy = APP_SIZE_HEIGHT;
		m_pcVireioGUI = new Vireio_GUI(sSizeOfThis, APP_FONT, APP_FONT_ITALIC, APP_SIZE_FONT, APP_COLOR_FONT, APP_COLOR_BACK);
		UINT dwPage = m_pcVireioGUI->AddPage();

		// control structure
		Vireio_Control sControl;

#ifdef _VIREIO_3
		// spin control - stereo view
		static std::vector<std::wstring> m_aszStereoViewOptions;
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		m_aszStereoViewOptions.push_back(L"Disabled - 0");
		m_aszStereoViewOptions.push_back(L"Side by Side - 20");
		m_aszStereoViewOptions.push_back(L"Over Under - 30");
		m_aszStereoViewOptions.push_back(L"Horizontal Interleave - 40");
		m_aszStereoViewOptions.push_back(L"Vertical Interleave - 50");
		m_aszStereoViewOptions.push_back(L"Checkerboard - 60");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Red/Cyan) - 1");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Red/Cyan) B+W - 2");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Yellow/Blue) - 5");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Yellow/Blue) B+W - 6");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Green/Magenta) - 10");
		m_aszStereoViewOptions.push_back(L"Anaglyph (Green/Magenta) B+W - 11");
		m_aszStereoViewOptions.push_back(L"DIY Rift - 100");
		m_aszStereoViewOptions.push_back(L"Oculus Rift: Direct Mode - 111");
		sControl.m_eControlType = Vireio_Control_Type::SpinControl;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 128;
		sControl.m_sSize.cx = 420;
		sControl.m_sSize.cy = APP_SIZE_FONT + 16;
		sControl.m_sSpinControl.m_dwCurrentSelection = 0;
		sControl.m_sSpinControl.m_paszEntries = &m_aszStereoViewOptions;
		m_dwSpinStereoView = m_pcVireioGUI->AddControl(dwPage, sControl);

		// spin control - tracker
		static std::vector<std::wstring> m_aszTrackerOptions;
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		m_aszTrackerOptions.push_back(L"No Tracking - 0");
		m_aszTrackerOptions.push_back(L"Hillcrest Labs - 10");
		m_aszTrackerOptions.push_back(L"FreeTrack - 20");
		m_aszTrackerOptions.push_back(L"Shared Memory Tracker - 30");
		m_aszTrackerOptions.push_back(L"OculusTrack - 40");
		sControl.m_eControlType = Vireio_Control_Type::SpinControl;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 188;
		sControl.m_sSize.cx = 420;
		sControl.m_sSize.cy = APP_SIZE_FONT + 16;
		sControl.m_sSpinControl.m_dwCurrentSelection = 0;
		sControl.m_sSpinControl.m_paszEntries = &m_aszTrackerOptions;
		m_dwSpinTracker = m_pcVireioGUI->AddControl(dwPage, sControl);

		// spin control - monitors
		static std::vector<std::wstring> m_aszMonitorOptions;
		ZeroMemory(&sControl, sizeof(Vireio_Control));

		// start to query for monitors
		UINT32 num_of_paths = 0;
		UINT32 num_of_modes = 0;
		DISPLAYCONFIG_PATH_INFO* displayPaths = NULL;
		DISPLAYCONFIG_MODE_INFO* displayModes = NULL;
		GetDisplayConfigBufferSizes(QDC_ALL_PATHS, &num_of_paths, &num_of_modes);

		// Allocate paths and modes dynamically
		displayPaths = (DISPLAYCONFIG_PATH_INFO*)calloc((int)num_of_paths, sizeof(DISPLAYCONFIG_PATH_INFO));
		displayModes = (DISPLAYCONFIG_MODE_INFO*)calloc((int)num_of_modes, sizeof(DISPLAYCONFIG_MODE_INFO));

		// Query for the information 
		QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &num_of_paths, displayPaths, &num_of_modes, displayModes, NULL);

		UINT32 index = 0;
		int adapterNum = 0;
		while (index < num_of_modes)
		{
			if (displayModes[index].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
			{
				DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName;
				DISPLAYCONFIG_DEVICE_INFO_HEADER header;
				header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
				header.adapterId = displayModes[index].adapterId;
				header.id = displayModes[index].id;
				header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
				deviceName.header = header;
				DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&deviceName);

				char monitorFriendlyDeviceName[256];
				ZeroMemory(monitorFriendlyDeviceName, 256);

				size_t countConverted = 0;
				wcstombs_s(&countConverted, monitorFriendlyDeviceName, 256, deviceName.monitorFriendlyDeviceName, 256);

				std::string szAdapter(monitorFriendlyDeviceName);
				std::wstring szAdapterW(szAdapter.begin(), szAdapter.end());
				if (adapterNum == 0) szAdapterW = L"Primary Monitor: " + szAdapterW + L" - 0";
				if (adapterNum == 1) szAdapterW = L"Secondary Monitor: " + szAdapterW + L" - 1";
				if (adapterNum == 2) szAdapterW = L"Tertiary Monitor: " + szAdapterW + L" - 2";
				m_aszMonitorOptions.push_back(szAdapterW);
				anMonitors.push_back(adapterNum);
				adapterNum++;
			}

			index++;
		}

		sControl.m_eControlType = Vireio_Control_Type::SpinControl;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 248;
		sControl.m_sSize.cx = 420;
		sControl.m_sSize.cy = APP_SIZE_FONT + 16;
		sControl.m_sSpinControl.m_dwCurrentSelection = 0;
		sControl.m_sSpinControl.m_paszEntries = &m_aszMonitorOptions;
		m_dwSpinMonitor = m_pcVireioGUI->AddControl(dwPage, sControl);
#else

		// spin control - game selection
		m_asVireioGameProfiles = std::vector<VireioGameProfile>();
		static std::vector<std::wstring> m_aszGames;
		ZeroMemory(&sControl, sizeof(Vireio_Control));

		// get the profiles
		bool profileFound = false;
		string profilePath = string("..\\cfg_v4\\profiles.xml");
		xml_document docProfiles;
		xml_parse_result resultProfiles = docProfiles.load_file(profilePath.c_str());
		xml_node profile;
		xml_node gameProfile;

		if (resultProfiles.status == status_ok)
		{
			xml_node xml_profiles = docProfiles.child("profiles");

			// loop through profiles
			for (xml_node profile = xml_profiles.child("profile"); profile; profile = profile.next_sibling("profile"))
			{
				VireioGameProfile sProfile;

				// get name
				sProfile.szGameName = profile.attribute("game_name").as_string();

				// get process
				sProfile.szGameProcess = profile.attribute("game_exe").as_string();
				sProfile.szGameProcess = StringToLower(sProfile.szGameProcess);

				// get architecture
				string szCPU = profile.attribute("cpu_architecture").as_string("32bit");

				// right cpu architecture ?
#ifdef _WIN64
				if (szCPU == string("64bit"))
				{
					sProfile.eCPU_Architecture = VireioGameProfile::CPU_Architecture_64bit;
#else
				if (szCPU == string("32bit"))
				{
					sProfile.eCPU_Architecture = VireioGameProfile::CPU_Architecture_32bit;
#endif
					// get dx version
					UINT unDX = profile.attribute("dx_version").as_uint(9);
					switch (unDX)
					{
#ifdef _WIN64
					case 9:
						sProfile.szProfileFilePath = profile.attribute("profile_path").as_string("..\\game_profiles\\HMD\\x64\\Mono\\Mono_Cinema_DX9.aqup");
						sProfile.eDX_Version = VireioGameProfile::DX_Version_9;
						break;
					case 10:
						sProfile.eDX_Version = VireioGameProfile::DX_Version_10;
						break;
					case 11:
						// get v4 profile path
						sProfile.szProfileFilePath = profile.attribute("profile_path").as_string("..\\game_profiles\\HMD\\x64\\Mono\\Mono_Cinema_DX11.aqup");
						sProfile.eDX_Version = VireioGameProfile::DX_Version_11;
						break;
					default:
						sProfile.eDX_Version = VireioGameProfile::DX_Version_9;
						break;
#else
					case 9:
						sProfile.szProfileFilePath = profile.attribute("profile_path").as_string("..\\game_profiles\\HMD\\x64\\Mono\\Mono_Cinema_DX9.aqup");
						sProfile.eDX_Version = VireioGameProfile::DX_Version_9;
						break;
					case 10:
						sProfile.eDX_Version = VireioGameProfile::DX_Version_10;
						break;
					case 11:
						// get v4 profile path
						sProfile.szProfileFilePath = profile.attribute("profile_path").as_string("..\\game_profiles\\HMD\\Win32\\Mono\\Mono_Cinema_DX11.aqup");
						sProfile.eDX_Version = VireioGameProfile::DX_Version_11;
						break;
					default:
						sProfile.eDX_Version = VireioGameProfile::DX_Version_9;
						break;
#endif
					};

					// get image url
					sProfile.szGameImageUrl = profile.attribute("image_url").as_string();

					// get time delay, repetition
					sProfile.unTimedelay = profile.attribute("time_delay").as_uint(0);
					sProfile.unRepetition = profile.attribute("repetition").as_uint(0);

					// and add to list
					m_asVireioGameProfiles.push_back(sProfile);
					std::wstring szName = std::wstring(sProfile.szGameName.begin(), sProfile.szGameName.end());
					m_aszGames.push_back(szName);
				}
			}
		}

		// Game selection
		sControl.m_eControlType = Vireio_Control_Type::ListBox;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 168;
		sControl.m_sSize.cx = 400;
		sControl.m_sSize.cy = APP_SIZE_FONT * 7;
		sControl.m_sListBox.m_bSelectable = true;
		sControl.m_sListBox.m_nCurrentSelection = unProfileIndex;
		sControl.m_sListBox.m_paszEntries = &m_aszGames;
		m_dwListGameProfiles = m_pcVireioGUI->AddControl(dwPage, sControl);

		// spin control - HMD selection
		static std::vector<std::wstring> m_aszHMD_Options;
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		m_aszHMD_Options.push_back(L"No HMD");
		m_aszHMD_Options.push_back(L"LibOVR");
		m_aszHMD_Options.push_back(L"OpenVR");
		// OSVR deprecated -> m_aszHMD_Options.push_back(L"OSVR");
		sControl.m_eControlType = Vireio_Control_Type::SpinControl;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 128;
		sControl.m_sSize.cx = 100;
		sControl.m_sSize.cy = APP_SIZE_FONT + 12;
		sControl.m_sSpinControl.m_dwCurrentSelection = unHMDIndex;
		sControl.m_sSpinControl.m_paszEntries = &m_aszHMD_Options;
		m_dwSpinHMD = m_pcVireioGUI->AddControl(dwPage, sControl);
#endif

		// create the main entries
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		static std::vector<std::wstring> sEntriesVersion;
		sControl.m_eControlType = Vireio_Control_Type::StaticListBox;
		sControl.m_sPosition.x = APP_SIZE_WIDTH - 270;
		sControl.m_sPosition.y = APP_SIZE_HEIGHT - APP_SIZE_FONT - 6;
		sControl.m_sSize.cx = APP_SIZE_WIDTH;
		sControl.m_sSize.cy = APP_SIZE_HEIGHT - 100;
		sControl.m_sStaticListBox.m_bSelectable = false;
		sControl.m_sStaticListBox.m_paszEntries = &sEntriesVersion;
		UINT dwVersion = m_pcVireioGUI->AddControl(dwPage, sControl);

		std::string date(__DATE__);
		std::string buildDate = date.substr(4, 2) + "-" + date.substr(0, 3) + "-" + date.substr(7, 4);

		// and add all entries
		std::wstringstream szStream = std::wstringstream();
		szStream << L"v" << APP_VERSION << " " << buildDate.c_str();
#ifdef _WIN64
		szStream << L" 64bit";
#else
		szStream << L" 32bit";
#endif
		m_pcVireioGUI->AddEntry(dwVersion, szStream.str().c_str());

#ifdef _VIREIO_3
		// oculus entry... only for v3
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		static std::vector<std::wstring> sEntriesOculus;
		sControl.m_eControlType = Vireio_Control_Type::StaticListBox;
		sControl.m_sPosition.x = 16;
		sControl.m_sPosition.y = 300;
		sControl.m_sSize.cx = APP_SIZE_WIDTH;
		sControl.m_sSize.cy = APP_SIZE_HEIGHT - 100;
		sControl.m_sStaticListBox.m_bSelectable = false;
		sControl.m_sStaticListBox.m_paszEntries = &sEntriesOculus;
		UINT dwOculus = m_pcVireioGUI->AddControl(dwPage, sControl);
		szStream = std::wstringstream();
		if (oculusProfile.Name.size())
		{
			szStream << "Oculus Profile : " << oculusProfile.Name.c_str();
			m_pcVireioGUI->AddEntry(dwOculus, szStream.str().c_str());
		}
#else
		// create the 'Load Aquilinus Profile' button
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		static std::wstring szLoadProfile = std::wstring(L"   Vireio Profile");
		sControl.m_eControlType = Vireio_Control_Type::Button;
		sControl.m_sPosition.x = 452;
		sControl.m_sPosition.y = 128;
		sControl.m_sSize.cx = 184 + 12; /**< +12 == border **/
		sControl.m_sSize.cy = 69 + 12; /**< +12 == border **/
		sControl.m_sButton.m_pszText = &szLoadProfile;
		m_dwLoadAquilinusProfile = m_pcVireioGUI->AddControl(dwPage, sControl);
#endif
		// create the 'x'(=exit) button
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		static std::wstring szX = std::wstring(L"x");
		sControl.m_eControlType = Vireio_Control_Type::Button;
		sControl.m_sPosition.x = APP_SIZE_WIDTH - 18;
		sControl.m_sPosition.y = 97;
		sControl.m_sSize.cx = 17;
		sControl.m_sSize.cy = 17;
		sControl.m_sButton.m_pszText = &szX;
		m_dwExit = m_pcVireioGUI->AddControl(dwPage, sControl);

		// create the '_'(=minimize) button
		ZeroMemory(&sControl, sizeof(Vireio_Control));
		static std::wstring szMin = std::wstring(L"~");
		sControl.m_eControlType = Vireio_Control_Type::Button;
		sControl.m_sPosition.x = APP_SIZE_WIDTH - 36;
		sControl.m_sPosition.y = 97;
		sControl.m_sSize.cx = 17;
		sControl.m_sSize.cy = 17;
		sControl.m_sButton.m_pszText = &szMin;
		m_dwMinimize = m_pcVireioGUI->AddControl(dwPage, sControl);

		// load the logo
		logo_bitmap = (HBITMAP)LoadImage(NULL, "..//img//logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		OutputDebugString("Load the bitmap\n");

		SetCursor(LoadCursor(NULL, IDC_ARROW));
		ShowWindow(window_handle, SW_SHOW);
		UpdateWindow(window_handle);

#ifndef _VIREIO_3
		/*// download game jpg files from steam if not present.. only for v4
		std::vector<std::string> aszImageURLs;
		std::vector<std::string> aszImageFilePaths;
		aszImageURLs.push_back("https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/377160/8977a8e98acbbdd3c0ff905afb7e0a6e2eb555ea.jpg");
		aszImageFilePaths.push_back("..//img//Fallout4.jpg");
		game_bitmap = nullptr;

		for (size_t nI = 0; nI < aszImageURLs.size(); nI++)
		{
			if (!PathFileExists(aszImageFilePaths[nI].c_str()))
			{
				HRESULT hr = URLDownloadToFile(NULL, aszImageURLs[nI].c_str(), aszImageFilePaths[nI].c_str(), 0, NULL);
				if (FAILED(hr))
				{
					OutputDebugString("Failed to download STEAM game image file !");
				}
			}
		}*/

		// load (injection state) button images
		std::vector<std::string> aszImageFilePaths;
		aszImageFilePaths.push_back("..//img//button_ix0.bmp");
		aszImageFilePaths.push_back("..//img//button_ix1.bmp");
		aszImageFilePaths.push_back("..//img//button_ix2.bmp");
		aszImageFilePaths.push_back("..//img//button_ix3.bmp");
		aszImageFilePaths.push_back("..//img//button_ix4.bmp");
		aszImageFilePaths.push_back("..//img//button_ix5.bmp");
		for (unsigned uI = 0; uI < 6; uI++)
		{
			button_bitmap[uI] = (HBITMAP)LoadImage(NULL, aszImageFilePaths[uI].c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		}
#endif

		// Refresh on timer for FPS readings
		SetTimer(window_handle, 1, 500, NULL);
	}
	~Vireio_Perception_Main_Window() {
		UnregisterClass(window_class_name, instance_handle);
	}

	/*** Vireio_Perception_Main_Window public members ***/
	static LRESULT WINAPI main_window_proc(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam);
	void                  run() { MSG window_message; while (GetMessage(&window_message, NULL, 0, 0)) { TranslateMessage(&window_message); DispatchMessage(&window_message); } }
};

/*** Vireio_Perception_Main_Window static fields ***/
POINT       Vireio_Perception_Main_Window::m_ptMouseCursor;
HBITMAP     Vireio_Perception_Main_Window::logo_bitmap;
//HBITMAP     Vireio_Perception_Main_Window::game_bitmap;
HBITMAP     Vireio_Perception_Main_Window::button_bitmap[6];
Vireio_GUI* Vireio_Perception_Main_Window::m_pcVireioGUI;
UINT        Vireio_Perception_Main_Window::m_dwListGameProfiles;
UINT        Vireio_Perception_Main_Window::m_dwSpinHMD;
UINT        Vireio_Perception_Main_Window::m_dwSpinStereoView;
UINT        Vireio_Perception_Main_Window::m_dwSpinTracker;
UINT        Vireio_Perception_Main_Window::m_dwSpinMonitor;
UINT        Vireio_Perception_Main_Window::m_dwLoadAquilinusProfile;
UINT        Vireio_Perception_Main_Window::m_dwExit;
UINT        Vireio_Perception_Main_Window::m_dwMinimize;

/**
* Main window procedure.
***/
LRESULT WINAPI Vireio_Perception_Main_Window::main_window_proc(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam)
{
	// call vireio gui class for all mouse actions
	if ((message == WM_KEYDOWN) || (message == WM_KEYUP) || (message == WM_LBUTTONDOWN) || (message == WM_LBUTTONUP) || (message == WM_MOUSEMOVE))
	{
		Vireio_GUI_Event sEvent = m_pcVireioGUI->WindowsEvent(message, wparam, lparam, 1);

		switch (sEvent.eType)
		{
		case ChangedToNext:
		case ChangedToPrevious:
		case ChangedToValue:
#ifdef _VIREIO_3
			if (sEvent.dwIndexOfControl == m_dwSpinStereoView)
			{
				ProxyHelper helper = ProxyHelper();
				helper.SaveUserConfig(anStereoModes[sEvent.dwNewValue]);
			}
			else if (sEvent.dwIndexOfControl == m_dwSpinTracker)
			{
				ProxyHelper helper = ProxyHelper();
				helper.SaveTrackerMode(anTrackerModes[sEvent.dwNewValue]);
			}
			else if (sEvent.dwIndexOfControl == m_dwSpinMonitor)
			{
				ProxyHelper helper = ProxyHelper();
				helper.SaveDisplayAdapter(anMonitors[sEvent.dwNewValue]);
			}
#else
			if (sEvent.dwIndexOfControl == m_dwListGameProfiles)
			{
				// get global config
#ifdef _WIN64
				string configPath = string("..\\cfg_v4\\config_64bit.xml");
#else
				string configPath = string("..\\cfg_v4\\config_32bit.xml");
#endif
				xml_document docConfig;
				xml_parse_result resultConfig = docConfig.load_file(configPath.c_str());

				if (resultConfig.status == status_ok)
				{
					xml_node xml_config = docConfig.child("config");

					if (sEvent.dwNewValue < (UINT)m_asVireioGameProfiles.size())
						xml_config.attribute("profile_index") = sEvent.dwNewValue;

					docConfig.save_file(configPath.c_str());
				}
			}
			if (sEvent.dwIndexOfControl == m_dwSpinHMD)
			{
				// get global config
#ifdef _WIN64
				string configPath = string("..\\cfg_v4\\config_64bit.xml");
#else
				string configPath = string("..\\cfg_v4\\config_32bit.xml");
#endif
				xml_document docConfig;
				xml_parse_result resultConfig = docConfig.load_file(configPath.c_str());

				if (resultConfig.status == status_ok)
				{
					xml_node xml_config = docConfig.child("config");

					if (sEvent.dwNewValue < 4)
						xml_config.attribute("hmd_index") = sEvent.dwNewValue;

					docConfig.save_file(configPath.c_str());
				}
			}
#endif
			break;
		case Pressed:
			if (sEvent.dwIndexOfControl == m_dwExit)
				SendMessage(window_handle, WM_CLOSE, 0, 0);
			if (sEvent.dwIndexOfControl == m_dwMinimize)
				ShowWindow(window_handle, SW_MINIMIZE);
#ifndef _VIREIO_3
			else if (sEvent.dwIndexOfControl == m_dwLoadAquilinusProfile)
			{
				g_bLoadAquilinusProfile = true;
			}
#endif
			break;
		default:
			break;
		}

		// send WM_PAINT
		if (message != WM_MOUSEMOVE)
			UpdateWindow(window_handle);
	}

	switch (message)
	{
	case WM_MOUSEMOVE:
		// move the window ?
		if (wparam & MK_LBUTTON)
		{
			// get new mouse cursor point
			LONG nX = (LONG)GET_X_LPARAM(lparam);
			LONG nY = (LONG)GET_Y_LPARAM(lparam);

			if (m_ptMouseCursor.y < 94)
			{
				// get the difference to the old position set only in WM_LBUTTONDOWN
				nX -= m_ptMouseCursor.x;
				nY -= m_ptMouseCursor.y;

				// get the old window position
				RECT rcWnd;
				GetWindowRect(window_handle, &rcWnd);

				// set the new window position
				SetWindowPos(window_handle, HWND_TOPMOST, rcWnd.left + nX, rcWnd.top + nY, 0, 0, SWP_NOSIZE);
			}
		}
		UpdateWindow(window_handle);
		return 0;
	case WM_CREATE:
	{
		OutputDebugString("Create Window\n");
		break;
	}
	case WM_TIMER:
	{
		RECT client_rect;
		GetClientRect(window_handle, &client_rect);
		InvalidateRect(window_handle, &client_rect, FALSE);
		return 0;
	}
	case WM_LBUTTONDOWN:
	{
		// get the mouse cursor position
		m_ptMouseCursor.x = (LONG)GET_X_LPARAM(lparam);
		m_ptMouseCursor.y = (LONG)GET_Y_LPARAM(lparam);
		break;
	}
	case WM_LBUTTONUP:
	{
#ifndef _VIREIO_3
		if (g_hmAquilinusRTE)
		{
			InjectionState eIS = (InjectionState)g_pAquilinus_GetInjectionState();
			if ((g_bLoadAquilinusProfile) && (eIS))
			{
				if ((eIS == InjectionState::Idle) || (eIS == InjectionState::Injecting))
				{
					OutputDebugString("Vireio Perception: Load Aquilinus Profile!");

					// get current selections
					UINT unSelectionGame = (UINT)m_pcVireioGUI->GetCurrentSelection(m_dwListGameProfiles);
					if (unSelectionGame >= (UINT)m_asVireioGameProfiles.size()) unSelectionGame = 0;
					UINT unSelectionHMD = (UINT)m_pcVireioGUI->GetCurrentSelection(m_dwSpinHMD);
					if (unSelectionHMD >= 4) unSelectionHMD = 0;
					OutputDebugString(m_asVireioGameProfiles[unSelectionGame].szGameName.c_str());

					// get profile path and set HMD string
					std::wstring szProfilePath = std::wstring(m_asVireioGameProfiles[unSelectionGame].szProfileFilePath.begin(), m_asVireioGameProfiles[unSelectionGame].szProfileFilePath.end());
					std::string szBasePath = getCurrentPath();
					szProfilePath = std::wstring(szBasePath.begin(), szBasePath.end()) + szProfilePath.substr(3);
					switch (unSelectionHMD)
					{
					case 0:
						szProfilePath = szProfilePath.substr(0, szProfilePath.find(L"HMD")) + L"Monitor" + szProfilePath.substr(szProfilePath.find(L"HMD") + 3);
						break;
					case 1:
						szProfilePath = szProfilePath.substr(0, szProfilePath.find(L"HMD")) + L"LibOVR" + szProfilePath.substr(szProfilePath.find(L"HMD") + 3);
						break;
					case 2:
						szProfilePath = szProfilePath.substr(0, szProfilePath.find(L"HMD")) + L"OpenVR" + szProfilePath.substr(szProfilePath.find(L"HMD") + 3);
						break;
						/* OSVR deprecated -> case 3:
							szProfilePath = szProfilePath.substr(0, szProfilePath.find(L"HMD")) + L"OSVR" + szProfilePath.substr(szProfilePath.find(L"HMD") + 3);
							break;*/
					}
					OutputDebugStringW(szProfilePath.c_str());

					// get process
					std::wstring szProcess = std::wstring(m_asVireioGameProfiles[unSelectionGame].szGameProcess.begin(), m_asVireioGameProfiles[unSelectionGame].szGameProcess.end());

					// load aquilinus profile
					LPCWSTR szP;
					g_pAquilinus_LoadProfile(szProfilePath.c_str(), szProcess.c_str(), &szP, m_asVireioGameProfiles[unSelectionGame].unRepetition, m_asVireioGameProfiles[unSelectionGame].unTimedelay);
					std::wstring szPath = std::wstring(szP);

					//// load game logo... TODO !! LOAD BY PROFILE !!
					//std::wstring szFilename = szPath.substr(szPath.find_last_of('\\') + 1);
					//if (szFilename.find(L"Fallout4") == 0)
					//{
					//	ConvertJpgFile("..//img//Fallout4.jpg", "..//img//game_logo.bmp");
					//	game_bitmap = (HBITMAP)LoadImage(NULL, "..//img//game_logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
					//}
				}
				else
					g_pAquilinus_Reinject();

				g_bLoadAquilinusProfile = false;
			}
		}
#endif
		break;
	}
	case WM_RBUTTONUP:
	{
#ifndef _VIREIO_3
		if (g_hmAquilinusRTE)
		{
			g_pAquilinus_ForceIdle();
			g_bLoadAquilinusProfile = false;
		}
#endif
		break;
	}
	case WM_PAINT:
	{
		PAINTSTRUCT 	ps;
		HDC 			hdc;
		BITMAP 		bitmap;
		HDC 			hdcMem;

		hdc = BeginPaint(window_handle, &ps);

		// draw GUI
		hdcMem = CreateCompatibleDC(hdc);
		HBITMAP hGUI = m_pcVireioGUI->GetGUI(true, false, true, true);
		SelectObject(hdcMem, hGUI);
		GetObject(hGUI, sizeof(bitmap), &bitmap);
		BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);

		// draw logo
		SelectObject(hdcMem, logo_bitmap);
		GetObject(logo_bitmap, sizeof(bitmap), &bitmap);
		BitBlt(hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
		DeleteDC(hdcMem);

#ifndef _VIREIO_3
		// draw profile button + close icon + minimize icon
		SelectObject(hdc, GetStockObject(DC_PEN));
		SelectObject(hdc, GetStockObject(DC_BRUSH));
		g_eInjectionState = (InjectionState)g_pAquilinus_GetInjectionState();
		switch (g_eInjectionState)
		{
		case Initial:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_INIT);
			break;
		case Idle:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_IDLE);
			break;
		case Injecting:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_INJECTING);
			break;
		case ToInject:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_TOINJECT);
			break;
		case Injected:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_INJECTED);
			break;
		case Closed:
			SetDCPenColor(hdc, APP_BUTTON_PENCOLOR_CLOSED);
			break;
		default:
			break;
		}
		SetDCBrushColor(hdc, APP_BUTTON_BRUSHCOLOR);
		Rectangle(hdc, 456, 132, 456 + 188, 132 + 73);
		SetDCPenColor(hdc, APP_BUTTON_PENCOLOR);
		Rectangle(hdc, APP_SIZE_WIDTH - 18, bitmap.bmHeight, APP_SIZE_WIDTH - 1, bitmap.bmHeight + 19);
		Rectangle(hdc, APP_SIZE_WIDTH - 36, bitmap.bmHeight, APP_SIZE_WIDTH - 19, bitmap.bmHeight + 19);
		SetDCBrushColor(hdc, APP_BUTTON_PENCOLOR);
		Rectangle(hdc, APP_SIZE_WIDTH - 12, bitmap.bmHeight + 7, APP_SIZE_WIDTH - 7, bitmap.bmHeight + 12);
		Rectangle(hdc, APP_SIZE_WIDTH - 32, bitmap.bmHeight + 14, APP_SIZE_WIDTH - 23, bitmap.bmHeight + 16);

		//// draw game logo
		//if (game_bitmap)
		//{
		//	hdcMem = CreateCompatibleDC(hdc);
		//	SelectObject(hdcMem, game_bitmap);
		//	GetObject(game_bitmap, sizeof(bitmap), &bitmap);
		//	BitBlt(hdc, 458, 134, 184, 69, hdcMem, 0, 0, SRCCOPY);
		//	DeleteDC(hdcMem);
		//}
		// draw game logo
		if (button_bitmap[(int)g_eInjectionState])
		{
			hdcMem = CreateCompatibleDC(hdc);
			SelectObject(hdcMem, button_bitmap[(int)g_eInjectionState]);
			GetObject(button_bitmap[(int)g_eInjectionState], sizeof(bitmap), &bitmap);
			BitBlt(hdc, 458, 134, 184, 69, hdcMem, 0, 0, SRCCOPY);
			DeleteDC(hdcMem);
		}
#endif

		EndPaint(window_handle, &ps);

		/*
		// Now get FPS from the registry
		HKEY hKey;
		LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\Vireio\\Perception", 0, KEY_ALL_ACCESS, &hKey);
		if (openRes == ERROR_SUCCESS)
		{
		char fpsBuffer[10];
		memset(fpsBuffer, 0, 10);
		DWORD dwDataSize = 10;
		LONG lResult = RegGetValue(hKey, NULL, "FPS", RRF_RT_REG_SZ, NULL, (LPVOID)&fpsBuffer, &dwDataSize);
		if (lResult == ERROR_SUCCESS)
		{
		TextOut(paint_device_context, 420, 183, fpsBuffer, (int)strlen(fpsBuffer));
		//Now delete, if this isn't refreshed, then we'll report blank next time round
		RegDeleteValue(hKey, "FPS");
		RegCloseKey(hKey);
		}
		else
		TextOut(paint_device_context, 420, 183, "--", 2);
		}

		// output extended profile data if aquilinus runtime environment present
		if (g_hmAquilinusRTE)
		{
		SetRect(&This->extended_profile_rectangle, 16, 190, 272, 254);
		}

		*/
		return 0;
	}
	case WM_SIZE:
	{
		InvalidateRect(window_handle, NULL, TRUE);
		break;
	}
	case WM_CLOSE:
	{
		PostQuitMessage(0);
#ifndef _VIREIO_3
		if (g_hmAquilinusRTE)
		{
			g_pAquilinus_Close();
			FreeLibrary(g_hmAquilinusRTE);
		}
#endif
		break;
	}
	}
	return DefWindowProc(window_handle, message, wparam, lparam);
};

/**
* Main windows entry point
***/
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {

	// avoid double driver window
	HWND window = FindWindow("perception", "Vireio Perception");
	if (window != 0)
	{
		OutputDebugString("Vireio Perception is already present !");
		return 0;
	}

	// save perception path to registry (for all versions)
	InitConfig();

#ifdef _VIREIO_3
	InitModes();
	InstallHook();
#else
	// load Aquilinus Runtime Environment, init driver
	if (LoadAquilinusRTE()) OutputDebugString("Vireio Perception : Using Aquilinus Runtime Environment.");
#endif

	// create window entries
	Vireio_Perception_Main_Window main_window("perception");

#ifdef _VIREIO_3
	// load the user configuration
	ProxyHelper helper = ProxyHelper();
	ProxyHelper::UserConfig userConfig;
	helper.LoadUserConfig(userConfig);
#endif

	// run main window
	main_window.run();

#ifdef _VIREIO_3
	// remove hook, exit
	RemoveHook();
#endif

	return 0;
}

/**
* Init registry configuration.
***/
bool InitConfig(void)
{
	std::string basePath = getCurrentPath();
	OutputDebugString("Current Path: ");
	OutputDebugString(basePath.c_str());
	OutputDebugString("\n");

	HKEY hKey;
	LPCTSTR sk = TEXT("SOFTWARE\\Vireio\\Perception");

	LONG createRes = RegCreateKeyEx(HKEY_CURRENT_USER, sk, 0, NULL, 0, 0, NULL, &hKey, NULL);

	if (createRes == ERROR_SUCCESS)
	{
		OutputDebugString("Success creating Registry.\n");
	}
	else
	{
		OutputDebugString("Error creating Registry.\n");
	}

	LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey);

	if (openRes == ERROR_SUCCESS)
	{
		OutputDebugString("Success opening key.\n");
	}
	else
	{
		OutputDebugString("Error opening key.\n");
	}

	LPCTSTR value = TEXT("BasePath");
	LPCTSTR data = basePath.c_str();

	LONG setRes = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, (int)strlen(data) + 1);

	if (setRes == ERROR_SUCCESS)
	{
		OutputDebugString("Success writing to Registry.\n");
	}
	else
	{
		OutputDebugString("Error writing to Registry.\n");
	}

	LONG closeOut = RegCloseKey(hKey);

	if (closeOut == ERROR_SUCCESS)
	{
		OutputDebugString("Success closing key.\n");
	}
	else
	{
		OutputDebugString("Error closing key.\n");
	}

	return true;
}

#ifdef _VIREIO_3
/**
* Creates simple vectors to easily set the Vireio proxy config.
***/
bool InitModes()
{
	anStereoModes.push_back(0);
	anStereoModes.push_back(20);
	anStereoModes.push_back(30);
	anStereoModes.push_back(40);
	anStereoModes.push_back(50);
	anStereoModes.push_back(60);
	anStereoModes.push_back(1);
	anStereoModes.push_back(2);
	anStereoModes.push_back(5);
	anStereoModes.push_back(6);
	anStereoModes.push_back(10);
	anStereoModes.push_back(11);
	anStereoModes.push_back(100);
	anStereoModes.push_back(111);

	anTrackerModes.push_back(0);
	anTrackerModes.push_back(10);
	anTrackerModes.push_back(20);
	anTrackerModes.push_back(30);
	anTrackerModes.push_back(40);

	return true;
}

#else
/**
* Loads the Aquilinus runtime environment, if present.
* @returns True if the Aquilinus runtime environment is present and loaded.
***/
bool LoadAquilinusRTE()
{
	// explicit Aquilinus Runtime Environment dll import
#ifdef _WIN64
	g_hmAquilinusRTE = LoadLibrary("AquilinusRTE_x64.dll");
#else
	g_hmAquilinusRTE = LoadLibraryW(L"AquilinusRTE_Win32.dll");
#endif

	// get Aquilinus Runtime Environment methods
	if (g_hmAquilinusRTE != NULL)
	{
		// get methods explicit
		g_pAquilinus_Init = (AQUILINUS_Init)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_Init");
		g_pAquilinus_Close = (AQUILINUS_Close)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_Close");
		g_pAquilinus_ForceIdle = (AQUILINUS_ForceIdle)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_ForceIdle");
		g_pAquilinus_LoadProfile = (AQUILINUS_LoadProfile)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_LoadProfile");
		g_pAquilinus_Reinject = (AQUILINUS_Reinject)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_Reinject");
		g_pAquilinus_GetInjectionState = (AQUILINUS_GetInjectionState)GetProcAddress(g_hmAquilinusRTE, "Aquilinus_GetInjectionState");

		if ((!g_pAquilinus_Init) || (!g_pAquilinus_Close) || (!g_pAquilinus_ForceIdle) || (!g_pAquilinus_LoadProfile) || (!g_pAquilinus_Reinject) || (!g_pAquilinus_GetInjectionState))
		{
			OutputDebugString("Aquilinus Runtime Environment method(s) not found !");
			FreeLibrary(g_hmAquilinusRTE);
			g_hmAquilinusRTE = NULL;
			return false;
		}
		else
			g_pAquilinus_Init();
	}
	else
	{
		OutputDebugString("Aquilinus Runtime not found !");
		return false;
	}

	return true;
}
#endif

/**
* Returns the path of Vireio Perception.
***/
std::string getCurrentPath(void)
{
	TCHAR fullPath[1024];
	int len = 1024;
	int bytes = GetModuleFileName(NULL, fullPath, len);
	if (bytes == 0)
	{
		OutputDebugString("GetModuleFileName FAILS !");
		return false;
	}

	std::string pathName = std::string(fullPath);
#ifdef _WIN64
	std::string exeName = "bin\\Perception_x64.exe";
#else
	std::string exeName = "bin\\Perception_Win32.exe";
#endif
	std::string basePath = "";
	size_t found = pathName.find(exeName);

	if (found != std::string::npos)
	{
		basePath = pathName.substr(0, int(found));
	}

	return basePath;
	}