#include "match.h"
#include "engine.h"
#include "main.h"
#include "defs.h"
#include "native/pchooks.h"

extern const char* HIJACK_ERROR;

uintptr_t processStartAddress, processStopAddress;

namespace Engine
{
	WCHAR* processName, // cached
		processPath[MAX_PATH]; // cached

	char configFileData[1000]{};

	bool UnsafeDetermineEngineType();

	// jichi 10/21/2014: Return whether found the game engine
	bool DetermineEngineType()
	{
		// jichi 9/27/2013: disable game engine for debugging use
		bool found = false;
#ifndef ITH_DISABLE_ENGINE
		__try { found = UnsafeDetermineEngineType(); }
		__except (EXCEPTION_EXECUTE_HANDLER) { ConsoleOutput(HIJACK_ERROR); }
#endif // ITH_DISABLE_ENGINE
		if (!found) { // jichi 10/2/2013: Only enable it if no game engine is detected
			PcHooks::hookOtherPcFunctions();
		} //else
		//  ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
		return found;
	}

	void Hijack()
	{
		static auto _ = ([]
		{
			GetModuleFileNameW(nullptr, processPath, MAX_PATH);
			processName = wcsrchr(processPath, L'\\') + 1;
			wchar_t configFilename[MAX_PATH + std::size(GAME_CONFIG_FILE)];
			wcsncpy_s(configFilename, processPath, MAX_PATH - 1);
			wcscpy_s(wcsrchr(configFilename, L'\\') + 1, std::size(GAME_CONFIG_FILE), GAME_CONFIG_FILE);
			if (AutoHandle<> configFile = CreateFileW(configFilename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))
			{
				ReadFile(configFile, configFileData, sizeof(configFileData) - 1, DUMMY, nullptr);
				if (strnicmp(configFileData, "engine:", 7) == 0)
				{
					if (const char* config = strchr(configFileData, '\n')) *(char*)(loadedConfig = config)++ = 0;
					ConsoleOutput("Textractor: Engine = %s", requestedEngine = strlwr(configFileData + 7));
				}
				else loadedConfig = configFileData;
				if (!*loadedConfig || strstr(configFileData, "https://")) loadedConfig = "";
				else ConsoleOutput("Textractor: game configuration loaded");
			}

			processStartAddress = processStopAddress = (uintptr_t)GetModuleHandleW(nullptr);
			MEMORY_BASIC_INFORMATION info;
			do
			{
				VirtualQuery((void*)processStopAddress, &info, sizeof(info));
				processStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
			} while (info.Protect > PAGE_NOACCESS);
			processStopAddress -= info.RegionSize;
			spDefault.minAddress = processStartAddress;
			spDefault.maxAddress = processStopAddress;
			ConsoleOutput("Textractor: hijacking process located from 0x%p to 0x%p", processStartAddress, processStopAddress);

			if (!strstr(requestedEngine, "none")) DetermineEngineType();
			if (processStartAddress + 0x40000 > processStopAddress) ConsoleOutput("Textractor: WARNING injected process is very small, possibly a dummy!");
		}(), 0);
	}

	bool ShouldMonoHook(const char* name)
	{
		if (!*loadedConfig) return strstr(name, "string:") && !strstr(name, "string:mem");
		for (const char* hook = loadedConfig; hook; hook = strchr(hook + 1, '\t'))
			for (auto start = name; *start; ++start)
				for (int i = 0; ; ++i)
					if (start[i] != hook[i + 1]) break;
					else if (!hook[i + 2] || hook[i + 2] == '\t') return true;
		return false;
	}
}
