text: #pragma comment(linker, "/EXPORT:DirectDrawCreate=_DirectDrawCreate@12")
  
  #include "DirectDrawWrapper.h"
  #include "resource.h"
  #include "detours.h"
  #include <stdio.h>
  #include <math.h>
  
  // Main thread ddrawwrapper object
  IDirectDrawWrapper *lpDD = NULL;
  // Original setcursorpos function pointer
  static BOOL (WINAPI *TrueSetCursorPos)(int,int) = SetCursorPos;
  static HMODULE (WINAPI *TrueLoadLibraryA)(LPCSTR) = GetModuleHandleA;
  
  // Are we in the settings menu
  BOOL inMenu;
  // Dll start time
  DWORD start_time;
  // The level of debug to display
  int debugLevel;
  //debug display mode (-1 = none, 0 = console, 1 = file)
  int debugDisplay;
  //the debug file handle
  FILE *debugFile;
  
  // Dll hmodule
  HMODULE hMod;
  
  /* Helper function for throwing debug/error messages
   *
   * int level      - Debug level
   * char *location - Message location
   * char *message  - Message
   */
  void debugMessage(int level, char *location, char *message)
  {
  	// If above the current level then skip totally
  	if(level > debugLevel) return;
  
      // Calculate HMS
  	DWORD cur_time = GetTickCount() - start_time;
  	long hours = (long)floor((double)cur_time / (double)3600000.0);
  	cur_time -= (hours * 3600000);
  	int minutes = (int)floor((double)cur_time / (double)60000.0);
  	cur_time -= (minutes * 60000);
  	double seconds = (double)cur_time / (double)1000.0;
  
      // Build error message
  	char text[4096] = "\0";
  	if(level == 0)
  	{
  		sprintf_s(text, 4096, "%d:%d:%#.1f ERR %s %s\n", hours, minutes, seconds, location, message);
  	}
  	else if(level == 1)
  	{
  		sprintf_s(text, 4096, "%d:%d:%#.1f WRN %s %s\n", hours, minutes, seconds, location, message);
  	}
  	else if(level == 2)
  	{
  		sprintf_s(text, 4096, "%d:%d:%#.1f INF %s %s\n", hours, minutes, seconds, location, message);
  	}
      // Output and flush
  	printf_s(text);
  	fflush(stdout);
  }
  
  // Override function for cursor position
  BOOL WINAPI OverrideSetCursorPos(int X, int Y)
  {
  	// If ddraw object exists and windowed mode
  	if(lpDD != NULL && lpDD->isWindowed)
  	{
  		// X,Y are relative to client area within the code
  		// Get client area location
  		POINT cpos;
  		cpos.x = 0;
  		cpos.y = 0;
  		ClientToScreen(lpDD->hWnd, &cpos);
  
  		// Calculate correct cursor offset and move
  		BOOL res = TrueSetCursorPos(cpos.x + X, cpos.y + Y);
  		return res;
  	}
  	return TrueSetCursorPos(X, Y);
  }
  
  // Override function for load library
  /*HMODULE WINAPI OverrideLoadLibraryA(LPCSTR lpModuleName)
  {
  	printf_s("%s\n", lpModuleName);
  	return TrueLoadLibraryA(lpModuleName);
  }*/
  
  // Pretty standard winproc
  LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
      switch (message)
      {
          // On syskey down
  		case WM_SYSKEYDOWN:
  			// ALT+ENTER trap keydown
  			if (wParam == VK_RETURN)
  			{
  				return 0;
  			}
  			break;
          // On syskey up
  		case WM_SYSKEYUP:
  			// ALT+ENTER trap keyup
  			if (wParam == VK_RETURN)
  			{
  				// If DDW exists
  				if(lpDD != NULL)
  				{
  					lpDD->ToggleFullscreen();
  				}
  				return 0;
  			}
  			break;
          // On keydown
  		case WM_KEYDOWN:
  			// Overload printscreen(save a snapshot of the current screen
  			if(wParam == VK_SNAPSHOT)
  			{
  				return 0;
  			}
  			// Always pass ~ to menu
  			if(wParam == VK_OEM_3)
  			{
  				return 0;
  			}
  			// Everything gets passed if we are in the menu
  			if(inMenu)
  			{
  				return 0;
  			}
  			break;
          // On keyup
  		case WM_KEYUP:
  			// Overload printscreen(save a snapshot of the current screen)
  			if(!inMenu && wParam == VK_SNAPSHOT)
  			{
  				lpDD->DoSnapshot();
  				return 0;
  			}
  			// Always pass ~ to menu
  			if(wParam == VK_OEM_3)
  			{
  				// Pass to menu and set inMenu to result
  				inMenu = lpDD->MenuKey(VK_OEM_3);
  				return 0;
  			}
  			// Everything gets passed if we are in the menu
  			if(inMenu)
  			{
  				// Pass to menu and set inMenu to result
  				inMenu = lpDD->MenuKey(wParam);
  				return 0;
  			}
  			break;
          // On destroy window
  		case WM_DESTROY:
  			// Restore the orignal window proc if we have it
  			if(lpDD->lpPrevWndFunc != NULL)
  			{
  				SetWindowLong(hwnd, GWL_WNDPROC, (LONG)lpDD->lpPrevWndFunc);
  			}
  			break;
      }
  	// Call original windiow proc by default
      return CallWindowProc(lpDD->lpPrevWndFunc, hwnd, message, wParam, lParam);
  }
  
  // Main dll entry
  BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
  {
  	LONG error;
  
  	switch (ul_reason_for_call)
  	{
  	// Initial process attach
  	case DLL_PROCESS_ATTACH:
  		// Store module handle
  		hMod = hModule;
  
  		// Set default variables
  		debugLevel = 0;
     		debugDisplay = -1;
  
  		// Set time start
  		start_time = GetTickCount();
  
  		// Not in menu to start
  		inMenu = false;
  		
          // Retrieve command line arguments
  		LPWSTR *szArgList;
  		int argCount;
  		szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
  		
          // If arguments
          if(szArgList != NULL)
  		{
  			for(int i = 0; i < argCount; i++)
  			{
                  // If debug
  				if(wcscmp(szArgList[i], L"/ddrawdebug") == 0) {
  					debugDisplay = 0;
  					debugLevel = 2;
  					// Create the debug console
  					AllocConsole();
  					// Redirect stdout to console
  					freopen( "CONOUT$", "wb", stdout);
  					break;
  				}
                  // If ddrawlog
  				else if(wcscmp(szArgList[i], L"/ddrawlog") == 0) {
  					debugDisplay = 1;
  					debugLevel = 2;
  					// Redireect stdout to file
  					char curPath[MAX_PATH];
  					char filename[MAX_PATH];
  					GetCurrentDirectoryA(MAX_PATH, curPath);
  					sprintf_s(filename, MAX_PATH, "%s\\ddraw_debug.log", curPath);
  					freopen_s(&debugFile, filename, "wb", stdout);
  					break;
  				}
  			}
  		}
  		LocalFree(szArgList);
          
  		// Hook setcursorpos
  		DetourRestoreAfterWith();
          DetourTransactionBegin();
          DetourUpdateThread(GetCurrentThread());
          DetourAttach(&(PVOID&)TrueSetCursorPos, OverrideSetCursorPos);
  		//DetourAttach(&(PVOID&)TrueLoadLibraryA, OverrideLoadLibraryA);
          error = DetourTransactionCommit();
          if (error == NO_ERROR) {
              debugMessage(2, "DllMain(DLL_PROCESS_ATTACH)", "Successfully detoured SetCursorPos");
          }
          else {
              debugMessage(1, "DllMain(DLL_PROCESS_ATTACH)", "Failed to detour SetCursorPos");
          }
  		break;
  	case DLL_THREAD_ATTACH:
  		// Do nothing on thread attach
  		break;
  	case DLL_THREAD_DETACH:
  		// Do nothing on thread detach
  		break;
  	case DLL_PROCESS_DETACH:		
  		// Delete DirectDrawWrapper object
  		delete lpDD;
  
  		// Detach function hook
  		DetourTransactionBegin();
          DetourUpdateThread(GetCurrentThread());
          DetourDetach(&(PVOID&)TrueSetCursorPos, OverrideSetCursorPos);
          error = DetourTransactionCommit();
  
          
  		//cleanup debug console or file if exists
  		if(debugDisplay == 0)
  		{
  			FreeConsole();
  		}
  		else if(debugDisplay == 1)
  		{
  			fclose(debugFile);
  		}
          
  		break;
  	}
  	return TRUE;
  }
  
  // Emulated direct draw create
  extern HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnknown FAR* pUnkOuter)
  {
  	// Create directdraw object
  	lpDD = new IDirectDrawWrapper();
  	if(lpDD == NULL) 
  	{
  		debugMessage(0, "DirectDrawCreate", "Failed to create IDirectDrawWrapper.");
  		return DDERR_OUTOFMEMORY;  // Simulate OOM error
  	}
  	
  	// Initialize the ddraw object with new wndproc
  	HRESULT hr = lpDD->WrapperInitialize(&WndProc, hMod);
  	// If error then return error(message will have been taken care of already)
  	if(hr != DD_OK) return hr;
  
  	// Set return pointer to the newly created DirectDrawWrapper interface
  	*lplpDD = (LPDIRECTDRAW)lpDD;
  	
  	// Return success
  	return DD_OK;
  }