#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <stdio.h>
#include <Shlwapi.h>
#include <cstdint>
#include "MemoryMgr.h"

extern "C" HRESULT WINAPI DirectDrawCreateEx(GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter)
{
	static HRESULT	(WINAPI *pDirectDrawCreateEx)(GUID FAR*, LPVOID*, REFIID, IUnknown FAR*);
	static bool		bLoaded = false;
	if ( !bLoaded )
	{
		wchar_t		wcSystemPath[MAX_PATH];
		GetSystemDirectoryW(wcSystemPath, MAX_PATH);
		PathAppendW(wcSystemPath, L"ddraw.dll");

		HMODULE		hLib = LoadLibraryW(wcSystemPath);
		pDirectDrawCreateEx = (HRESULT(WINAPI*)(GUID FAR*, LPVOID*, REFIID, IUnknown FAR*))GetProcAddress(hLib, "DirectDrawCreateEx");

		static char		aNoDesktopMode[64];

		RECT			desktop;
		GetWindowRect(GetDesktopWindow(), &desktop);
		sprintf(aNoDesktopMode, "Cannot find %dx%dx32 video mode", desktop.right, desktop.bottom);

		using namespace MemoryVP;

		if (*(DWORD*)0x5C1E70 == 0x53E58955)
		{
			// III 1.0
			Patch<DWORD>(0x581E5E, desktop.right);
			Patch<DWORD>(0x581E68, desktop.bottom);
			Patch<BYTE>(0x581E72, 32);
			Patch<const char*>(0x581EA8, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x581411, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x5812D6, 0xB8);
			Patch<DWORD>(0x5812D7, 0x900);
		}
		else if (*(DWORD*)0x5C2130 == 0x53E58955)
		{
			// III 1.1
			Patch<DWORD>(0x58219E, desktop.right);
			Patch<DWORD>(0x5821A8, desktop.bottom);
			Patch<BYTE>(0x5821B2, 32);
			Patch<const char*>(0x5821E8, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x581753, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x581620, 0xB8);
			Patch<DWORD>(0x581621, 0x900);
		}
		else if (*(DWORD*)0x5C6FD0 == 0x53E58955)
		{
			// III Steam
			Patch<DWORD>(0x58208E, desktop.right);
			Patch<DWORD>(0x582098, desktop.bottom);
			Patch<BYTE>(0x5820A2, 32);
			Patch<const char*>(0x5820D8, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x581653, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x581520, 0xB8);
			Patch<DWORD>(0x581521, 0x900);
		}

		else if (*(DWORD*)0x667BF0 == 0x53E58955)
		{
			// VC 1.0
			Patch<DWORD>(0x600E7E, desktop.right);
			Patch<DWORD>(0x600E88, desktop.bottom);
			Patch<BYTE>(0x600E92, 32);
			Patch<const char*>(0x600EC8, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x601E26, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x601CA0, 0xB8);
			Patch<DWORD>(0x601CA1, 0x900);
		}
		else if (*(DWORD*)0x667C40 == 0x53E58955)
		{
			// VC 1.1
			Patch<DWORD>(0x600E9E, desktop.right);
			Patch<DWORD>(0x600EA8, desktop.bottom);
			Patch<BYTE>(0x600EB2, 32);
			Patch<const char*>(0x600EE8, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x601E56, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x601CD0, 0xB8);
			Patch<DWORD>(0x601CD1, 0x900);
		}
		else if (*(DWORD*)0x666BA0 == 0x53E58955)
		{
			// VC Steam
			Patch<DWORD>(0x600ADE, desktop.right);
			Patch<DWORD>(0x600AE8, desktop.bottom);
			Patch<BYTE>(0x600AF2, 32);
			Patch<const char*>(0x600B28, aNoDesktopMode);

			// No 12mb vram check
			Patch<BYTE>(0x601A96, 0xEB);

			// No DirectPlay dependency
			Patch<BYTE>(0x601910, 0xB8);
			Patch<DWORD>(0x601911, 0x900);
		}

		// Give _rwcseg proper access rights
		HMODULE					hModule = GetModuleHandle(nullptr);
		IMAGE_NT_HEADERS*		ntHeader = (IMAGE_NT_HEADERS*)((ptrdiff_t)hModule + ((IMAGE_DOS_HEADER*)hModule)->e_lfanew);
		WORD					NumberOfSections = ntHeader->FileHeader.NumberOfSections;
		IMAGE_SECTION_HEADER*	pSection = IMAGE_FIRST_SECTION(ntHeader);

		for ( WORD i = 0; i < NumberOfSections; i++ )
		{
			if ( *(uint64_t*)(pSection->Name) == 0x006765736377725F )	// _rwcseg
			{
				DWORD	dwProtect;
				VirtualProtect((LPVOID)((ptrdiff_t)hModule + pSection->VirtualAddress), pSection->Misc.VirtualSize, PAGE_EXECUTE_READ, &dwProtect);
				break;
			}
			pSection++;
		}

		bLoaded = true;
	}
	return pDirectDrawCreateEx(lpGUID, lplpDD, iid, pUnkOuter);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	UNREFERENCED_PARAMETER(hinstDLL);
	UNREFERENCED_PARAMETER(fdwReason);
	UNREFERENCED_PARAMETER(lpvReserved);

	return TRUE;
}