// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。
#include "stdafx.h"

//#define USE_LOG
//#define USE_LOG_MORE

BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID);
void DllInit();
void DllExit();

void $_StreamReaderThread();
void ProxOpenFile();

HANDLE WINAPI CreateFile_Hook(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
HANDLE (WINAPI *CreateFile_Real)(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);

#define patch(a, v, s) _patch((void*)(a), (void*)(v), (s))
void _patch(void*, void*, int);
#define nop(a, s) _nop((void*)(a), (s))
void _nop(void*, int);
bool check(void*, unsigned char, const char*, bool);
bool _check(void*, unsigned char);

typedef struct _VirtualIMG
{
	int iType;
	char cIMGPath[MAX_PATH];
	char cDirPath[MAX_PATH];
	HANDLE hIMGFile;
	HANDLE hNewFile;
	int iIMGFile;
	char* cIMGFile;
} VirtualIMG;

int iIMGCount;
VirtualIMG* pVirtualIMG;

int iAttachCount;
char cSelfPath[MAX_PATH];

#ifdef USE_LOG
FILE* pLogFile;
#endif

char* cOriginalFile;
int loop_i;
char cReplaceFile[MAX_PATH];
const char* cVImgExt = ".vimg";
unsigned long dwEDI;
unsigned long dwRealOpenFile;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		if (iAttachCount++ == 0) DllInit();
		break;
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		if (--iAttachCount == 0) DllExit();
		break;
	}
	return TRUE;
}

void DllInit()
{
#ifdef USE_LOG
	fopen_s(&pLogFile, "vimg.log", "w");
#endif

	{
		GetModuleFileName(NULL, cSelfPath, MAX_PATH);
		if (strrchr(cSelfPath, '\\')) *(char*)(strrchr(cSelfPath, '\\') + 1) = '\0';

		char cFilePath[MAX_PATH];
		strcpy_s(cFilePath, MAX_PATH, cSelfPath);
		strcat_s(cFilePath, MAX_PATH, "virtualimg.dat");

		FILE* pFile;
		if (fopen_s(&pFile, cFilePath, "r"))
		{
			MessageBox(0, "Failed to open virtualimg.dat.", "Virtual IMG", MB_OK | MB_ICONERROR);
			ExitProcess(0);
		}

		char cType[16];
		int iType;
		char cIMGPath[MAX_PATH];
		char cDirPath[MAX_PATH];

		while (fscanf_s(pFile, "%s\t%s\t%s", cType, 16, cIMGPath, MAX_PATH, cDirPath, MAX_PATH) == 3)
		{
			iType = 0;
			if (!_stricmp(cType, "MARGE")) iType = 1;
			if (!_stricmp(cType, "CREATE")) iType = 2;
			if (!_stricmp(cType, "APPEND")) iType = 3;
			if (!_stricmp(cType, "IGNORE")) continue;

			pVirtualIMG = (VirtualIMG*)realloc(pVirtualIMG, sizeof(VirtualIMG) * (iIMGCount + 1));

			pVirtualIMG[iIMGCount].iType = iType;
			strcpy_s(pVirtualIMG[iIMGCount].cIMGPath, MAX_PATH, cIMGPath);
			strcpy_s(pVirtualIMG[iIMGCount].cDirPath, MAX_PATH, cDirPath);
			pVirtualIMG[iIMGCount].hIMGFile = 0;
			pVirtualIMG[iIMGCount].hNewFile = 0;
			pVirtualIMG[iIMGCount].iIMGFile = 0;
			pVirtualIMG[iIMGCount].cIMGFile = NULL;

			if (!pVirtualIMG[iIMGCount].iType) continue;

			strcpy_s(cIMGPath, MAX_PATH, cSelfPath);
			strcat_s(cIMGPath, MAX_PATH, pVirtualIMG[iIMGCount].cIMGPath);
			strcpy_s(cFilePath, MAX_PATH, cIMGPath);
			strcat_s(cFilePath, MAX_PATH, ".vimg");

			pVirtualIMG[iIMGCount].hIMGFile = CreateFile(cIMGPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);

			HANDLE hNewIMG;
			HANDLE hFind;
			WIN32_FIND_DATA ffd;
			unsigned long dwCurrentFile = 0x00800000;
			unsigned long dwVersion;
			unsigned long dwFileNum;
			unsigned long dwBuffer[8];
			unsigned long dwSize;
			unsigned long dwIMGFile;

			hNewIMG = CreateFile(cFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL);

			ReadFile(pVirtualIMG[iIMGCount].hIMGFile, &dwVersion, 0x4, &dwSize, NULL);
			WriteFile(hNewIMG, &dwVersion, 0x4, &dwSize, NULL);

			dwFileNum = 0;
			WriteFile(hNewIMG, &dwFileNum, 0x4, &dwSize, NULL);

			strcpy_s(cDirPath, MAX_PATH, cSelfPath);
			strcat_s(cDirPath, MAX_PATH, pVirtualIMG[iIMGCount].cDirPath);
			strcat_s(cDirPath, MAX_PATH, "*");

			pVirtualIMG[iIMGCount].iIMGFile = 0;

			hFind = FindFirstFile(cDirPath, &ffd);
			while (true)
			{
				if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
				{
					if (!FindNextFile(hFind, &ffd)) break;
					continue;
				}
				dwBuffer[0] = dwCurrentFile;
				dwBuffer[1] = (ffd.nFileSizeLow >> 11 | ffd.nFileSizeHigh << 21) + 1;
				ZeroMemory(&dwBuffer[2], 0x18);
				strcpy_s((char*)&dwBuffer[2], 0x18, strrchr(ffd.cFileName, '\\') ? strrchr(ffd.cFileName, '\\') + 1 : ffd.cFileName);
				WriteFile(hNewIMG, dwBuffer, 0x20, &dwSize, NULL);
				pVirtualIMG[iIMGCount].cIMGFile =(char*)realloc(pVirtualIMG[iIMGCount].cIMGFile, MAX_PATH * (pVirtualIMG[iIMGCount].iIMGFile + 1));
				strcpy_s(&pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * pVirtualIMG[iIMGCount].iIMGFile], MAX_PATH, cSelfPath);
				strcat_s(&pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * pVirtualIMG[iIMGCount].iIMGFile], MAX_PATH, pVirtualIMG[iIMGCount].cDirPath);
				strcat_s(&pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * pVirtualIMG[iIMGCount].iIMGFile], MAX_PATH, ffd.cFileName);
				pVirtualIMG[iIMGCount].iIMGFile++;
				dwFileNum++;
				dwCurrentFile++;
				if (!FindNextFile(hFind, &ffd)) break;
			}
			FindClose(hFind);

			switch (pVirtualIMG[iIMGCount].iType)
			{
			case 1:
				{
					ReadFile(pVirtualIMG[iIMGCount].hIMGFile, &dwIMGFile, 0x4, &dwSize, NULL);
					for (unsigned long i = 0; i < dwIMGFile; i++)
					{
						ReadFile(pVirtualIMG[iIMGCount].hIMGFile, dwBuffer, 0x20, &dwSize, NULL);
						for (int j = 0; j < pVirtualIMG[iIMGCount].iIMGFile; j++) if (!_stricmp((const char*)&dwBuffer[2], (strrchr(&pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * j], '\\')) ? strrchr(&pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * j], '\\') + 1 : &pVirtualIMG[iIMGCount].cIMGFile[MAX_PATH * j]))
						{
							dwBuffer[1] = 0;
							break;
						}
						if (!dwBuffer[1]) continue;
						WriteFile(hNewIMG, dwBuffer, 0x20, &dwSize, NULL);
						dwFileNum++;
					}
					break;
				}
			case 2:
				{
					break;
				}
			case 3:
				{
					ReadFile(pVirtualIMG[iIMGCount].hIMGFile, &dwIMGFile, 0x4, &dwSize, NULL);
					for (unsigned long i = 0; i < dwIMGFile; i++)
					{
						ReadFile(pVirtualIMG[iIMGCount].hIMGFile, dwBuffer, 0x20, &dwSize, NULL);
						WriteFile(hNewIMG, dwBuffer, 0x20, &dwSize, NULL);
						dwFileNum++;
					}
					break;
				}
			}

			CloseHandle(pVirtualIMG[iIMGCount].hIMGFile);
			pVirtualIMG[iIMGCount].hIMGFile = CreateFile(cIMGPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_RANDOM_ACCESS, NULL);

			SetFilePointer(hNewIMG, 0x4, NULL, FILE_BEGIN);
			WriteFile(hNewIMG, &dwFileNum, 0x4, &dwSize, NULL);
			CloseHandle(hNewIMG);

			iIMGCount++;
		}
	}

	unsigned char cJump = 0xE9;
	unsigned long pFunc;

	if (check((void*)0x4069B4, 0x68, "StreamReader - CreateThread", false))
	{
		pFunc = (unsigned long)&$_StreamReaderThread;
		patch(0x4069B5, &pFunc, 4);
	}

	if (check((void*)0x1564AE2, 0x68, "FileManager - OpenFile", false))
	{
		dwRealOpenFile = *(unsigned long*)0x538901 + 0x538905;
		patch(0x538900, &cJump, 1);
		pFunc = (unsigned long)&ProxOpenFile - 0x538905;
		patch(0x538901, &pFunc, 4);
	}

	{
		DWORD dwBase;
		DWORD dwIdataSize;
		PIMAGE_IMPORT_DESCRIPTOR pImgDesc;
		char* lpModule;
		PIMAGE_IMPORT_BY_NAME pImportName;

		dwBase = (DWORD)GetModuleHandle(NULL);
		pImgDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData((HMODULE)dwBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &dwIdataSize);
		while(pImgDesc->Name)
		{
			lpModule = (char*)(dwBase+pImgDesc->Name);
			if (!_stricmp(lpModule, "KERNEL32.dll")) break;
			pImgDesc++;
		}
		if (pImgDesc->Name)
		{
			PIMAGE_THUNK_DATA pIAT, pINT;
			pIAT = (PIMAGE_THUNK_DATA)(dwBase + pImgDesc->FirstThunk);
			pINT = (PIMAGE_THUNK_DATA)(dwBase + pImgDesc->OriginalFirstThunk);
			while(pIAT->u1.Function)
			{
				if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)) continue;
				pImportName = (PIMAGE_IMPORT_BY_NAME)(dwBase + (DWORD)pINT->u1.AddressOfData);
				if (!_stricmp((const char*)pImportName->Name, "CreateFileA"))
				{
					CreateFile_Real = *(HANDLE (WINAPI **)(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE))pIAT;
					pFunc = (unsigned long)&CreateFile_Hook;
					patch(pIAT, &pFunc, 4);
				}
				pIAT++;
				pINT++;
			}
		}
	}

	return;
}

void DllExit()
{
	for (int i = 0; i < iIMGCount; i++)
	{
		if (!pVirtualIMG[iIMGCount].iType) continue;

		CloseHandle(pVirtualIMG[i].hIMGFile);
		free(pVirtualIMG[i].cIMGFile);

		char cFilePath[MAX_PATH];
		strcpy_s(cFilePath, MAX_PATH, cSelfPath);
		strcat_s(cFilePath, MAX_PATH, pVirtualIMG[i].cIMGPath);
		strcat_s(cFilePath, MAX_PATH, ".vimg");

		DeleteFile(cFilePath);
	}

	free(pVirtualIMG);

#ifdef USE_LOG
	fclose(pLogFile);
#endif

	return;
}

typedef struct _StreamRW
{
	DWORD dwFilePos;
	DWORD dwFileSize;
	LPVOID pBuffer;
	BYTE bUnknown;
	BYTE bSemaphoreFlag;
	WORD wUseFlag;
	DWORD dwReadRequest;
	HANDLE hSemaphore;
	HANDLE hFile;
	OVERLAPPED olStruct;
} StreamRW;

DWORD* dwFileFlag = (DWORD*)0x8E3FE0;
DWORD* dwUnk8E3FE4 = (DWORD*)0x8E3FE4;
DWORD* dwEnableOverlap = (DWORD*)0x8E3FE8;
DWORD** pStreamIndex = (DWORD**)0x8E3FEC;
LONG* dwReqStream = (LONG*)0x8E3FF0;
LONG* dwCurStream = (LONG*)0x8E3FF4;
DWORD* dwMaxRW = (DWORD*)0x8E3FF8;
StreamRW** pStreamRW = (StreamRW**)0x8E3FFC;
DWORD* dwStreamThreadID = (DWORD*)0x8E4000;
HANDLE* hStreamSemaphore = (HANDLE*)0x8E4004;
HANDLE* hStreamThreadHandle = (HANDLE*)0x8E4008;
HANDLE* hStreamFile = (HANDLE*)0x8E4010;
CHAR* cStreamFilePath = (CHAR*)0x8E4098;
DWORD* dwMaxSteam = (DWORD*)0x8E4090;
HANDLE* hCurStreamFile = (HANDLE*)0x8E4898;
LPVOID** ppBuffer = (LPVOID**)0x8E4CAC;

void $_StreamReaderThread()
{
	LONG _StreamIndex;
	DWORD _ReadSize;
	HANDLE _DummyHandle;
	DWORD _DummyPos;

	while (true)
	{
		WaitForSingleObject(*hStreamSemaphore, INFINITE);
		_StreamIndex = (*dwReqStream == *dwCurStream) ? -1 : (*pStreamIndex)[*dwReqStream];
		(*pStreamRW)[_StreamIndex].wUseFlag = 1;
		if (!(*pStreamRW)[_StreamIndex].dwReadRequest)
		{
			_DummyPos = 0;
			for (int i = 0; i < iIMGCount; i++) if (pVirtualIMG[i].hNewFile == (*pStreamRW)[_StreamIndex].hFile) if ((*pStreamRW)[_StreamIndex].dwFilePos & 0x800000)
			{
#ifdef USE_LOG
				fputs("[Read Dummy]\t", pLogFile);
				fputs(&pVirtualIMG[i].cIMGFile[MAX_PATH * ((*pStreamRW)[_StreamIndex].dwFilePos & 0x7FFFFF)], pLogFile);
#endif
				_DummyHandle = (*pStreamRW)[_StreamIndex].hFile;
				(*pStreamRW)[_StreamIndex].hFile = CreateFile(&pVirtualIMG[i].cIMGFile[MAX_PATH * ((*pStreamRW)[_StreamIndex].dwFilePos & 0x7FFFFF)], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, ((*dwEnableOverlap) ? FILE_FLAG_OVERLAPPED : 0) | FILE_FLAG_RANDOM_ACCESS, NULL);
				_DummyPos = (*pStreamRW)[_StreamIndex].dwFilePos;
				(*pStreamRW)[_StreamIndex].dwFilePos = 0;
				ZeroMemory((*pStreamRW)[_StreamIndex].pBuffer, (*pStreamRW)[_StreamIndex].dwFileSize << 11);
				break;
			} else
			{
#ifdef USE_LOG
#ifdef USE_LOG_MORE
				fputs("[Read Dummy]\t", pLogFile);
				fputs(cSelfPath, pLogFile);
				fputs(pVirtualIMG[i].cIMGPath, pLogFile);
#endif
#endif
				_DummyHandle = (*pStreamRW)[_StreamIndex].hFile;
				(*pStreamRW)[_StreamIndex].hFile = pVirtualIMG[i].hIMGFile;
				_DummyPos = (*pStreamRW)[_StreamIndex].dwFilePos;
				break;
			}
			if (*dwEnableOverlap)
			{
				(*pStreamRW)[_StreamIndex].olStruct.Offset = (*pStreamRW)[_StreamIndex].dwFilePos << 11;
				(*pStreamRW)[_StreamIndex].olStruct.OffsetHigh = (*pStreamRW)[_StreamIndex].dwFilePos >> 21;
				if (ReadFile((*pStreamRW)[_StreamIndex].hFile, (*pStreamRW)[_StreamIndex].pBuffer, min((*pStreamRW)[_StreamIndex].dwFileSize << 11, GetFileSize((*pStreamRW)[_StreamIndex].hFile, NULL)), 0, &(*pStreamRW)[_StreamIndex].olStruct) == TRUE) (*pStreamRW)[_StreamIndex].dwReadRequest = 0; else
				{
					switch (GetLastError())
					{
					case ERROR_IO_PENDING:
						{
							if (GetOverlappedResult((*pStreamRW)[_StreamIndex].hFile, &(*pStreamRW)[_StreamIndex].olStruct, &_ReadSize, TRUE)) (*pStreamRW)[_StreamIndex].dwReadRequest = 0;
							else (*pStreamRW)[_StreamIndex].dwReadRequest = 0xFE;
							break;
						}
					case ERROR_NOACCESS:
						{
#ifdef USE_LOG
							char buf[256];
							sprintf_s(buf, 256, "[ERROR]\t%d\t0x%08X%08X-0x%08X", GetLastError(), (*pStreamRW)[_StreamIndex].olStruct.OffsetHigh, (*pStreamRW)[_StreamIndex].olStruct.Offset, (*pStreamRW)[_StreamIndex].dwFileSize << 11);
							fputs(buf, pLogFile);
							fputs("\n", pLogFile);
#endif
							if (MessageBox(0, "An ERROR_NOACCESS error has occured.\nAn .img file might have broken the limit.\nPlease press [Enter] to kill this process.", "GTA San Andreas", MB_OKCANCEL) == IDOK)
							{
								DllExit();
								ExitProcess(0);
							}
							(*pStreamRW)[_StreamIndex].dwReadRequest = 0xFE;
							break;
						}
					default:
						{
#ifdef USE_LOG
							char buf[256];
							sprintf_s(buf, 256, "[ERROR]\t%d\t0x%08X%08X-0x%08X", GetLastError(), (*pStreamRW)[_StreamIndex].olStruct.OffsetHigh, (*pStreamRW)[_StreamIndex].olStruct.Offset, (*pStreamRW)[_StreamIndex].dwFileSize << 11);
							fputs(buf, pLogFile);
							fputs("\n", pLogFile);
#endif
							(*pStreamRW)[_StreamIndex].dwReadRequest = 0xFE;
							break;
						}
					}
				}
			} else
			{
				_ReadSize = (*pStreamRW)[_StreamIndex].dwFilePos >> 21;
				SetFilePointer((*pStreamRW)[_StreamIndex].hFile, (*pStreamRW)[_StreamIndex].dwFilePos << 11, (PLONG)&_ReadSize, FILE_BEGIN);
				(*pStreamRW)[_StreamIndex].dwReadRequest = (ReadFile((*pStreamRW)[_StreamIndex].hFile, (*pStreamRW)[_StreamIndex].pBuffer, min((*pStreamRW)[_StreamIndex].dwFileSize << 11, GetFileSize((*pStreamRW)[_StreamIndex].hFile, NULL)), 0, NULL)) ? 0 : 0xFE;
			}
			if (_DummyPos) if (_DummyPos & 0x800000)
			{
#ifdef USE_LOG
				fputs(((*pStreamRW)[_StreamIndex].dwReadRequest) ? "\tFailed\n" : "\tSuccessed\n", pLogFile);
#endif
				CloseHandle((*pStreamRW)[_StreamIndex].hFile);
				(*pStreamRW)[_StreamIndex].hFile = _DummyHandle;
				(*pStreamRW)[_StreamIndex].dwFilePos = _DummyPos;
			} else
			{
#ifdef USE_LOG
#ifdef USE_LOG_MORE
				fputs(((*pStreamRW)[_StreamIndex].dwReadRequest) ? "\tFailed\n" : "\tSuccessed\n", pLogFile);
#endif
#endif
				(*pStreamRW)[_StreamIndex].hFile = _DummyHandle;
				(*pStreamRW)[_StreamIndex].dwFilePos = _DummyPos;
			}
		}
		if (*dwReqStream != *dwCurStream) *dwReqStream = (*dwReqStream + 1) % *dwMaxRW;
		(*pStreamRW)[_StreamIndex].dwFileSize = 0;
		if ((*pStreamRW)[_StreamIndex].bSemaphoreFlag) ReleaseSemaphore((*pStreamRW)[_StreamIndex].hSemaphore, 1, NULL);
		(*pStreamRW)[_StreamIndex].wUseFlag = 0;
	}
}

#ifdef USE_LOG
const char* cPrefixI = "[Ignore]\t";
const char* cPrefix = "[Use Dummy]\t";
const char* cTab = "\t";
const char* cReturn = "\n";
#endif

void __declspec(naked) ProxOpenFile()
{
	__asm
	{
		mov dwEDI, edi;
		mov edi, dwRealOpenFile;
		push edi;
		push eax;
		push esi;
		mov edi, [esp+0x10];
		mov esi, iIMGCount;
		test esi, esi;
		jz IMG_EXIT;
IMG_LOOP:
		dec esi;
		mov eax, esi;
		imul eax, eax, 0x021C;
		add eax, 0x0004;
		add eax, pVirtualIMG;
		push eax;
		push edi;
		call _stricmp;
		add esp, 0x08;
		test eax, eax;
		jz IMG_BREAK;
		cmp eax, 0x20;
		je IMG_BREAK;
		test esi, esi;
		jnz IMG_LOOP;
IMG_EXIT:
#ifdef USE_LOG
#ifdef USE_LOG_MORE
		push pLogFile;
		push cPrefixI;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push edi;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push cReturn;
		call fputs;
		add esp, 0x08;
#endif
#endif
		pop esi;
		pop eax;
		pop edi;
		push edi;
		mov edi, dwEDI;
		ret;
IMG_BREAK:
		mov eax, esi;
		imul eax, eax, 0x021C;
		add eax, pVirtualIMG;
		add eax, 0x0004;
		push eax;
		push MAX_PATH;
		mov eax, OFFSET cReplaceFile;
		push eax;
		call strcpy_s;
		add esp, 0x0C;
		mov eax, cVImgExt;
		push eax;
		push MAX_PATH;
		mov eax, OFFSET cReplaceFile;
		push eax;
		call strcat_s;
		add esp, 0x0C;
#ifdef USE_LOG
		push pLogFile;
		push cPrefix;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push edi;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push cTab;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push OFFSET cReplaceFile;
		call fputs;
		add esp, 0x08;
		push pLogFile;
		push cReturn;
		call fputs;
		add esp, 0x08;
#endif
		pop esi;
		pop eax;
		mov edi, OFFSET cReplaceFile;
		mov [esp+0x08], edi;
		mov edi, dwEDI;
		ret;
	};
}

HANDLE WINAPI CreateFile_Hook(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
	for (int i = 0; i < iIMGCount; i++)
	{
		if (!pVirtualIMG[i].iType) continue;
		if (_stricmp(lpFileName, pVirtualIMG[i].cIMGPath) == '\0' || _stricmp(lpFileName, pVirtualIMG[i].cIMGPath) == ' ')
		{
			char cReplaceFile[MAX_PATH];
			HANDLE hFile;
			strcpy_s(cReplaceFile, MAX_PATH, pVirtualIMG[i].cIMGPath);
			strcat_s(cReplaceFile, MAX_PATH, ".vimg");
#ifdef USE_LOG
			fputs("[Use Dummy]\t", pLogFile);
			fputs(lpFileName, pLogFile);
			fputs("\t", pLogFile);
			fputs(cReplaceFile, pLogFile);
			fputs("\n", pLogFile);
#endif
			hFile = (*CreateFile_Real)(cReplaceFile, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
			if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) pVirtualIMG[i].hNewFile = hFile;
			return hFile;
		}
	}
#ifdef USE_LOG
#ifdef USE_LOG_MORE
	fputs("[Ignore]\t", pLogFile);
	fputs(lpFileName, pLogFile);
	fputs("\n", pLogFile);
#endif
#endif
	return (*CreateFile_Real)(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

void _patch(void* pAddress, void* pData, int iSize)
{
	unsigned long dwProtect[2];
	VirtualProtect(pAddress, iSize, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
	memcpy(pAddress, pData, iSize);
	VirtualProtect(pAddress, iSize, dwProtect[0], &dwProtect[1]);
}

void _nop(void* pAddress, int iSize)
{
	unsigned long dwProtect[2];
	VirtualProtect(pAddress, iSize, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
	memset(pAddress, 0x90, iSize);
	VirtualProtect(pAddress, iSize, dwProtect[0], &dwProtect[1]);
}

bool check(void* pAddress, unsigned char cByte, const char* sExp, bool bContinue)
{
	if (_check(pAddress, cByte)) return true;
	if (bContinue) return false;
	char buf[1024];
	sprintf_s(buf, 1024, "An unknown code found at 0x%08X (%s).\nDo you want to continue without this Function?", pAddress, sExp);
	if (MessageBox(0, buf, "Virtual IMG", MB_YESNO | MB_ICONERROR | MB_DEFBUTTON2) == IDNO) ExitProcess(0);
	return false;
}

bool _check(void* pAddress, unsigned char cByte)
{
	unsigned long dwProtect[2];
	unsigned char cValue = cByte;
	VirtualProtect(pAddress, 1, PAGE_EXECUTE_READ, &dwProtect[0]);
	memcpy(&cValue, pAddress, 1);
	VirtualProtect(pAddress, 1, dwProtect[0], &dwProtect[1]);
	return (cValue == cByte);
}
