/*
* HL Loader
* Copyright (c) goldfinder (goldfinder@ifrance.com) 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <windows.h>
#include <string>
#include <fstream>
#include <iostream>
#include "mempatcher.h"
#include "RedirectFunction.h"
#include "apihook.h"
#include "utils.h"
#include "cvar.h"
#include "GenericPatch.h"

extern char ogcdir[256];
extern char hldir[256];

using namespace std;

GenericPatch soundPatch;
CMemoryPatcher Patcher;
MODULEENTRY32 HLEntry;

const BYTE HL_DecodeDLL_SIG[] = "\x51\x53\x55\x8B\x6C\x24\x10\x56\x8B\x74\x24\x20\x57";
const DWORD HL_DecodeDLL_SIG_LEN = 13;

const BYTE HW_DecodeDLL_SIG[] = "\x51\x55\x8B\x6C\x24\x0C\x56\x8B\x74\x24\x1C\x57";
const DWORD HW_DecodeDLL_SIG_LEN = 12;

int( *pHL_DecodeDLL )( DWORD dwBaseAddr, char **szDllName, DWORD *ExportTable, int iSize );
int( *pHW_DecodeDLL )( DWORD dwBaseAddr, char **szDllName, DWORD *ExportTable, int iSize );

HMODULE xModule;

void mainlogit(string msg) 
{ 
	ofstream fout; 
	fout.open("d:\\MAINdebug.txt", ios::app); 

	if(fout.fail()) return; // error, quitting. 

	fout << msg << endl; 
	fout.close(); // save & close file 
} 

typedef BOOL    (WINAPI *QueryPerformanceCounterFunc)(LARGE_INTEGER*);
QueryPerformanceCounterFunc QueryPerformanceCounterPtr = NULL;

//speedhack code by noskill
DWORD last_real=0;
DWORD last_fake=0;
DWORD last_ticks=0;
DWORD speed_ticks_left=0;
DWORD phase_ticks_left=0;

BOOL WINAPI pQueryPerformanceCounter(LARGE_INTEGER *lp)
{

	BOOL  ret       = QueryPerformanceCounterPtr(lp);
    DWORD cur_ticks = timeGetTime();
	
	DWORD new_real  = lp->LowPart;
	if(!last_real){ last_real = last_fake = new_real; return ret; }

	// determine wether we are allowed to use speed hack (speed_ticks_left)
	DWORD ticks_passed = cur_ticks-last_ticks; 	last_ticks = cur_ticks;
	if (phase_ticks_left>ticks_passed) // phase running
	{
		phase_ticks_left -= ticks_passed;
		if(cvar.speed>1)
		{
			// consume speed ticks 
			if(speed_ticks_left>ticks_passed) { speed_ticks_left -= ticks_passed; }
			else                              { speed_ticks_left =0;              }
		}
	} else { // phase not running or just timed out
		if(cvar.speed>1)
		{   // setup a new phase
			speed_ticks_left = cvar.speedon;
			phase_ticks_left = cvar.speedon+cvar.speedoff;
		} else {
			phase_ticks_left = 0;		
		}
	}

	//===================
	double factor = (cvar.speed<1 ? 1:cvar.speed);
	if(!speed_ticks_left) { factor = 1.0; }

	DWORD diff_real = (DWORD)(new_real-last_real);
	DWORD diff_fake = (DWORD)(factor * double(diff_real));
	lp->LowPart     = last_fake + diff_fake;
	
	last_fake += diff_fake;
	last_real += diff_real;

	return ret;
}

inline static void lowercase(char* str)
{
        while(*str){ *str = tolower(*str); ++str; }
}

bool badfile(const char* filename)
{
        char   fullpath[400];
        char*  fileNameBase=NULL;
        if( !strstr(filename,"..") )                                   return false;// quick return
        if( GetFullPathName(filename,395,fullpath,&fileNameBase)>390 ) return false;
        
        *fileNameBase = 0;
        lowercase(fullpath); int accessdir_len = strlen(fullpath);
        lowercase(hldir);    int hldir_len     = strlen(hldir);
        lowercase(ogcdir);   int ogcdir_len    = strlen(ogcdir);
        
        int n = (hldir_len<accessdir_len)?hldir_len:accessdir_len;

        bool ret = false;
        if( strncmp(fullpath,hldir,n))           ret=true;
        if(!strncmp(fullpath,ogcdir,ogcdir_len)) ret=true;
        return ret;
}

void PreS_DynamicSound(const DWORD a,const DWORD entindex,const DWORD c,const char*const sample,const float*const origin,DWORD f,const DWORD g,const DWORD h);
static DWORD sound_reloc_addr1=0;
__declspec(naked) void JumpGate_DynamicSound()
{
    __asm{
        // 8 args on stack
		call PreS_DynamicSound;  

        // exec caller instructions & jump back
        sub esp,0x48;
        
        mov  eax, soundPatch.patchloc;
        add  eax, soundPatch.patchsize;
        push eax;
        
        mov  eax,sound_reloc_addr1;
        mov  eax,[eax];

        ret;
	}
}

//========================================================================================
bool detour_S_DynamicSound()
{
        static bool applied = false;
        if( applied )   { return true; }
		applied = true;
		//assert(stackret);

        BYTE* offs = (BYTE*)0x01D7E3B0;
		BYTE* sig   = (BYTE*)"\x83\xEC\x48\xA1\x00\x00\x00\x00";
		char* mask  = "xxxx????";

		
		if(!soundPatch.find_location(offs,sig,mask))
		{
				MessageBox(0,"bla","bla",0);
                return false;
		}
		
		// prepare patch:
		soundPatch.patchdata[0]=0x68;
		*(DWORD*)(soundPatch.patchdata+1) = (DWORD)JumpGate_DynamicSound;
		soundPatch.patchdata[5]=0xC3;
		soundPatch.patchdata[6]=0x90;
		soundPatch.patchdata[7]=0x90;

        // relocated addr needed in detour
		sound_reloc_addr1 = *(DWORD*)(soundPatch.backup+4);

        return true;
}

BOOL (WINAPI *Module32NextPtr) ( HANDLE hSnapshot, LPMODULEENTRY32 lpme );
BOOL WINAPI newModule32Next ( HANDLE hSnapshot, LPMODULEENTRY32 lpme )
{
  BOOL res = (*Module32NextPtr)(hSnapshot,lpme);
  if (lpme->hModule == xModule ) res = (*Module32NextPtr)(hSnapshot,lpme);
  return res;
}

HMODULE WINAPI newLoadLibrary ( LPCSTR lpLibFileName );
DWORD lloffset;
DWORD llpt=(DWORD)&newLoadLibrary;

typedef HINSTANCE (WINAPI *LoadLibraryFunc)(LPCTSTR);
LoadLibraryFunc LoadLibraryPtr;

FARPROC WINAPI newGetProcAddress ( HMODULE hModule, LPCSTR lpProcName );
DWORD gpoffset;
DWORD gppt=(DWORD)&newGetProcAddress;

typedef FARPROC (WINAPI *GetProcAddressFunc)(HMODULE,LPCTSTR);

FARPROC  WINAPI newGetProcAddress ( HMODULE hModule, LPCSTR lpProcName )
{
	FARPROC nResult;
	nResult = GetProcAddress(hModule, lpProcName);
	if(HIWORD(lpProcName))
	{
		if(!strcmp(lpProcName,"CreateFileA"))
		{
		
		}
		else if(!strcmp(lpProcName,"QueryPerformanceCounter"))
		{
			QueryPerformanceCounterPtr = (QueryPerformanceCounterFunc)GetProcAddress(hModule,lpProcName);
			return (FARPROC)pQueryPerformanceCounter;

		}
		else if(!strcmp(lpProcName,"GetProcAddress"))
		{
			return (FARPROC)newGetProcAddress;
            
		}
		else if(!strcmp(lpProcName,"GetMessageA"))
		{
	
		}
		else
		{
			void CheckForOpenGlHook(FARPROC* pProc,LPCTSTR lpProcName);
			CheckForOpenGlHook(&nResult, lpProcName);

			void CheckClientHook(FARPROC* pProc,LPCTSTR lpProcName);
			CheckClientHook(&nResult, lpProcName);
		}
	}
	return nResult;
}

//=====================================================================================================
void ReRoute(HMODULE res,PIMAGE_NT_HEADERS pNTHeader,DWORD* new_addr,char* modulename, char* funcname, DWORD* out_offset)
{
	//generic call to old_add patcher
	bool success = importoffset((char*)res,modulename,funcname,out_offset)!=0;
	if(!success)
	{
		MessageBox(0,funcname,"REROUTE FAILED",0);
		return;
	}
	BYTE *pos;
	BYTE* codestart=(BYTE*)res+pNTHeader->OptionalHeader.BaseOfCode;
	DWORD length=pNTHeader->OptionalHeader.SizeOfCode;
	while (ScanPattern_FF15calladr(codestart,*out_offset,length,&pos))
	{
		length-=pos+6-codestart;
		codestart=pos+6;
		Protect(pos+2,sizeof(DWORD));
		DWORD* blub0r=(DWORD*)(pos+2);
		*blub0r = (DWORD)new_addr; 
		Protect(pos+2,-1);
	}
	codestart=(BYTE*)res+pNTHeader->OptionalHeader.BaseOfCode;
	length=pNTHeader->OptionalHeader.SizeOfCode;
	while (ScanPattern_8B2Dcalladr(codestart,*out_offset,length,&pos))
	{
		length-=pos+6-codestart;
		codestart=pos+6;
		Protect(pos+2,sizeof(DWORD));
		DWORD* blub0r=(DWORD*)(pos+2);
		*blub0r = (DWORD)new_addr; 
		Protect(pos+2,-1);
	}
	codestart=(BYTE*)res+pNTHeader->OptionalHeader.BaseOfCode;
	length=pNTHeader->OptionalHeader.SizeOfCode;
	while (ScanPattern_8B35calladr(codestart,*out_offset,length,&pos))
	{
		length-=pos+6-codestart;
		codestart=pos+6;
		Protect(pos+2,sizeof(DWORD));
		DWORD* blub0r=(DWORD*)(pos+2);
		*blub0r = (DWORD)new_addr; 
		Protect(pos+2,-1);
	}
}

HMODULE WINAPI newLoadLibrary  ( LPCSTR lpLibFileName )
{
	LoadLibraryPtr = (LoadLibraryFunc)(*(DWORD*)lloffset); 
	HMODULE res = (*LoadLibraryPtr)(lpLibFileName); 
	if (strstr("client.dll",lpLibFileName))
	{
		InterceptDllCall(res,"Kernel32.dll","Module32Next",&newModule32Next,(VOID**)&Module32NextPtr,NULL);
	}
	return res;
}

void PatchClientFunc( DWORD *ClientFunc_ADDR );

int HW_DecodeDLL( DWORD dwBaseAddr, char **szDllName, DWORD *ExportTable, int iSize ) 
{ 
	DWORD retvalue; 
	static bool bAlreadyPatched = false; 

	retvalue = pHW_DecodeDLL( dwBaseAddr, szDllName, ExportTable, iSize ); 

	if( !bAlreadyPatched ) 
	{ 
		PatchClientFunc( ExportTable ); 
		bAlreadyPatched = true; 
	} 
	
	return( retvalue ); 
} 

int HL_DecodeDLL( DWORD dwBaseAddr, char **szDllName, DWORD *ExportTable, int iSize )
{
	DWORD retvalue;
	DWORD dwSegmentAddr;
	DWORD HW_DecodeDLL_ADDR;

	retvalue = pHL_DecodeDLL( dwBaseAddr, szDllName, ExportTable, iSize );

	dwSegmentAddr = *( ( DWORD * )( dwBaseAddr + 0x5C ) );
	
	HW_DecodeDLL_ADDR = Patcher.FindPattern( HLEntry.th32ProcessID, ( BYTE * )HW_DecodeDLL_SIG, HW_DecodeDLL_SIG_LEN, ( DWORD )HLEntry.modBaseAddr, HLEntry.modBaseSize );

	pHW_DecodeDLL = ( int( * )( DWORD, char **, DWORD *, int ) )RedirectFunction( (char *)HW_DecodeDLL_ADDR, HW_DecodeDLL_SIG_LEN, ( DWORD )&HW_DecodeDLL );

	return( retvalue );
}

bool PatchHL( void )
{
	DWORD HL_DecodeDLL_ADDR;

	if (!Patcher.Init())
		return (false);

	if (!Patcher.FindModuleInProcess( "hl.exe", GetCurrentProcessId(), &HLEntry))
		if (!Patcher.FindModuleInProcess( "cstrike.exe", GetCurrentProcessId(), &HLEntry))
			return (false);

	HL_DecodeDLL_ADDR = Patcher.FindPattern( HLEntry.th32ProcessID, ( BYTE * )HL_DecodeDLL_SIG, HL_DecodeDLL_SIG_LEN, ( DWORD )HLEntry.modBaseAddr, HLEntry.modBaseSize );
	
	if( !( pHL_DecodeDLL = ( int( * )( DWORD, char **, DWORD *, int ) )RedirectFunction( (char *)HL_DecodeDLL_ADDR, HL_DecodeDLL_SIG_LEN, ( DWORD )&HL_DecodeDLL ) ) )
		return( false );

	return( true );
}

bool WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
{
	switch( fdwReason ) 
	{ 
		case DLL_PROCESS_ATTACH :
		{
			xModule = hinstDLL;

			// setup path
			GetModuleFileName(hinstDLL,ogcdir,255);
			char* pos = ogcdir+strlen(ogcdir);
			while(pos>=ogcdir && *pos!='\\') --pos;
			pos[1]=0; 

			GetModuleFileName(GetModuleHandle(NULL),hldir,255);
			pos = hldir+strlen(hldir);
			while(pos>=hldir && *pos!='\\') --pos;
			pos[1]=0; 

		//no thread notification
		DisableThreadLibraryCalls(static_cast<HMODULE>(hinstDLL));
		
		//clear our headers so paladin wont find us
		PIMAGE_DOS_HEADER pDOSHeader = MakePtr<PIMAGE_DOS_HEADER>(hinstDLL); 
		PIMAGE_NT_HEADERS pNTHeader = MakePtr<PIMAGE_NT_HEADERS>(pDOSHeader,pDOSHeader->e_lfanew);
		RandomOverwrite(pDOSHeader);
		RandomOverwrite(pNTHeader);

		//get main module and create fake code copy
		HMODULE MainModule = GetModuleHandle(NULL);
		pDOSHeader = MakePtr<PIMAGE_DOS_HEADER>(MainModule); 
		pNTHeader = MakePtr<PIMAGE_NT_HEADERS>(pDOSHeader,pDOSHeader->e_lfanew);

		//generic call ds:LoadLibraryA patcher
		importoffset((char*)MainModule,"Kernel32.dll","LoadLibraryA",&lloffset);
		BYTE *posa;
		BYTE* codestart=(BYTE*)MainModule+pNTHeader->OptionalHeader.BaseOfCode;
		DWORD length=pNTHeader->OptionalHeader.SizeOfCode;
		while (ScanPattern_FF15calladr(codestart,lloffset,length,&posa))
		{
			length-=posa+6-codestart;
			codestart=posa+6;
			Protect(posa+2,sizeof(DWORD));
			DWORD* blub0r=(DWORD*)(posa+2);
			*blub0r = (DWORD)&llpt; 
			Protect(posa+2,-1);
		}

		ReRoute(MainModule, pNTHeader,&gppt,         "Kernel32.dll","GetProcAddress",&gpoffset);

			if( !PatchHL( ) )
				return( false );
			break;
		}
		case DLL_PROCESS_DETACH :
		{
			break;
		}
		case DLL_THREAD_ATTACH :
		{
			break;
		}
		case DLL_THREAD_DETACH :
		{
			break;
		}
	}
	return( true );
}
