/*	Renegade Scripts.dll
	Main loading code
	Copyright 2007 Datalore, Jonathan Wilson

	This file is part of the Renegade scripts.dll
	The Renegade scripts.dll 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, or (at your option) any later
	version. See the file COPYING for more details.
	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
	Only the source code to the module(s) containing the licenced code has to be released.
*/
#include "scripts.h"
#include "engine.h"
#ifndef WIN32
#include <dlfcn.h>
#include <time.h>
#endif

#include "Windows.h"
#include "YaRR.h"

#include "dllmain.h"

cs CreateScript;
ds DestroyScript;
gsc GetScriptCount;
gsn GetScriptName;
gspd GetScriptParamDescription;
srdf SetRequestDestroyFunc;
unsigned int OldCount;
ScriptCommandsClass Commands;
ssc SetScriptCommands;
#ifdef WIN32
HMODULE bhs;
int __cdecl __CxxSetUnhandledExceptionFilter(void);
FARPROC GetProcAddressCheck(HMODULE hModule,LPCSTR lpProcName)
{
	FARPROC x = GetProcAddress(hModule,lpProcName);
	if (!x)
	{
		MessageBox(NULL,"Error getting function address from bhs.dll",lpProcName,MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
	}
	return x;
}
#define Address GetProcAddressCheck
#else
#define Address dlsym
void *bhs;
#endif
void LoadScopes();
void FreeScopes();
void DestroyExpVehFacClass();
void DestroyVerifyFileController();
extern "C"
{
#ifdef WIN32
void NotifyHookUnhandledExceptionFilter()
{
	HMODULE _bhs = GetModuleHandle("bhs.dll");
	if (_bhs)
	{
		__CxxSetUnhandledExceptionFilter();
	}
}

bool WriteMemory(int Address, const void* Buffer, int Size) 
{
	bool ret = 1;
	DWORD OldProtect;
	HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
	VirtualProtectEx(Process, (LPVOID)Address, Size, PAGE_EXECUTE_READWRITE, &OldProtect);
	if (!WriteProcessMemory(Process, (LPVOID)Address, Buffer, Size, NULL)) {
		ret = 0;
	}
	VirtualProtectEx(Process, (LPVOID)Address, Size, OldProtect, NULL);
	CloseHandle(Process);
	return ret;
}


BOOL SCRIPTSAPI __stdcall DllMain(HINSTANCE hinstDLL, DWORD	ul_reason_for_call, LPVOID	lpReserved)
{
	int LastError;
	unsigned char tmp = 0xC3;
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			WriteMemory(0x006A7C49, (void *)&tmp, 1);
			DisableThreadLibraryCalls(hinstDLL);
			if (Exe == 6)
			{
				InitEngine();
			}
			if ((!Exe) || (Exe == 1))
			{
				if (GetFileAttributes("shaders.dll") == 0xFFFFFFFF)
				{
					FILE *f = fopen("dllload.txt","at");
					if (f)
					{
						fprintf(f,"[shaders.dll] Module not found, unable to continue.\n");
						fclose(f);
					}
					MessageBox(NULL,"shaders.dll was not found. Are you sure you installed scripts.dll correctly?","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
					ExitProcess(1);
				}
				if (GetFileAttributes("d3d8.dll") == 0xFFFFFFFF)
				{
					FILE *f = fopen("dllload.txt","at");
					if (f)
					{
						fprintf(f,"[d3d8.dll] Module not found, unable to continue.\n");
						fclose(f);
					}
					MessageBox(NULL,"d3d8.dll was not found. Are you sure you installed scripts.dll correctly?","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
					ExitProcess(1);
				}
				if (GetFileAttributes("bhs.dll") == 0xFFFFFFFF)
				{
					FILE *f = fopen("dllload.txt","at");
					if (f)
					{
						fprintf(f,"[bhs.dll] Module not found, unable to continue.\n");
						fclose(f);
					}
					MessageBox(NULL,"bhs.dll was not found. Are you sure you installed scripts.dll correctly?","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
					ExitProcess(1);
				}
				bhs = LoadLibrary("bhs.dll");
				LastError = GetLastError();
				if (!bhs)
				{
					FILE *f = fopen("dllload.txt","at");
					if (f)
					{
						char *errorMessage = new char[2048]; //Ugly, but whatever.
						FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,LastError,0,errorMessage,2048,NULL);
						fprintf(f,"[bhs.dll] Failed to load, error %d. %s\n",LastError,errorMessage);
						delete[] errorMessage;
						fclose(f);
					}
					MessageBox(NULL,"Unfortunately, there was an error loading bhs.dll. Please report this bug!","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
					ExitProcess(1);
				}
				atexit(NotifyHookUnhandledExceptionFilter);
			}
			if (GetFileAttributes("scripts2.dll") == 0xFFFFFFFF)
			{
				FILE *f = fopen("dllload.txt","at");
				if (f)
				{
					fprintf(f,"[scripts2.dll] Module not found, unable to continue.\n");
					fclose(f);
				}
				MessageBox(NULL,"scripts2.dll was not found. Are you sure you installed scripts.dll correctly?","Error",MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
				ExitProcess(1);
			}
			wwscripts = LoadLibrary("scripts2.dll");
			LastError = GetLastError();
			if (wwscripts)
			{
				CreateScript = (cs) GetProcAddress(wwscripts,"Create_Script");
				DestroyScript = (ds) GetProcAddress(wwscripts,"Destroy_Script");
				GetScriptCount = (gsc) GetProcAddress(wwscripts,"Get_Script_Count");
				GetScriptName = (gsn) GetProcAddress(wwscripts,"Get_Script_Name");
				GetScriptParamDescription = (gspd) GetProcAddress(wwscripts,"Get_Script_Param_Description");
				SetScriptCommands = (ssc) GetProcAddress(wwscripts,"Set_Script_Commands");
				SetRequestDestroyFunc = (srdf) GetProcAddress(wwscripts,"Set_Request_Destroy_Func");
				OldCount = (GetScriptCount) ();
			}
			else
			{
				FILE *f = fopen("dllload.txt","at");
				if (f)
				{
					char *errorMessage = new char[2048]; //Ugly, but whatever.
					FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,LastError,0,errorMessage,2048,NULL);
					fprintf(f,"[scripts2.dll] Failed to load, error %d. %s\n",LastError,errorMessage);
					delete[] errorMessage;
					fclose(f);
				}
				MessageBox(NULL,"Unfortunately, there was an error loading scripts2.dll. Please report this bug!","Error",MB_OK|MB_ICONEXCLAMATION);
				ExitProcess(1);
			}
			break;
		case DLL_PROCESS_DETACH:
			YaRR_Unload();
			DestroyEngineMath();
			FreeScopes();
			DestroyExpVehFacClass();
			DestroyVerifyFileController();
			FreeLibrary(wwscripts);
#if DEBUG
			m_dumpMemoryReport("memreport_scripts.log",false);
#endif
			break;
	}
	return TRUE;
}
#endif

ScriptClass SCRIPTSAPI *Create_Script(const char *ScriptName)
{
	if (!ScriptName)
	{
		return 0;
	}
	ScriptClass *scr = ScriptRegistrar::CreateScript(ScriptName);
	if (scr)
	{
		return scr;
	}
	return (CreateScript) (ScriptName);
}

void SCRIPTSAPI Destroy_Script(ScriptClass *scr)
{
	if (scr)
	{
		delete scr;
	}
}

unsigned int SCRIPTSAPI Get_Script_Count()
{
	int sn = 0;
	sn = ScriptRegistrar::Count();
	return (GetScriptCount) () + sn;
}

const char SCRIPTSAPI *Get_Script_Name(unsigned int Number)
{
	if (Number < OldCount)
	{
		return (GetScriptName) (Number);
	}
	int n = Number - OldCount;
	return ScriptRegistrar::GetScriptFactory(n)->GetName();
}

const char SCRIPTSAPI *Get_Script_Param_Description(unsigned int Number)
{
	if (Number < OldCount)
	{
		return (GetScriptParamDescription) (Number);
	}
	int n = Number - OldCount;
	return ScriptRegistrar::GetScriptFactory(n)->GetParamDescription();
}



bool SCRIPTSAPI Set_Script_Commands(ScriptCommandsClass *ScriptCommands)
{
	Commands = *ScriptCommands;
	if ((Commands->version1 != 816) || (Commands->version2 != 174)) //if these dont match it means the ScriptCommands dont match with what is expected and the DLL cant work with whatever client is trying to use it
	{
		return false;
	}
	Commands->Enable_Stealth = (_Enable_Stealth)Address(bhs,"New_Enable_Stealth");
	Commands->Create_Explosion = (_Create_Explosion)Address(bhs,"New_Create_Explosion");
	Commands->Create_Explosion_At_Bone = (_Create_Explosion_At_Bone)Address(bhs,"New_Create_Explosion_At_Bone");
	Commands->Set_Fog_Enable = (_Set_Fog_Enable)Address(bhs,"New_Set_Fog_Enable");
	Commands->Set_Fog_Range = (_Set_Fog_Range)Address(bhs,"New_Set_Fog_Range");
	Commands->Set_War_Blitz = (_Set_War_Blitz)Address(bhs,"New_Set_War_Blitz");
	Commands->Fade_Background_Music = (_Fade_Background_Music)Address(bhs,"New_Fade_Background_Music");
	Commands->Set_Background_Music = (_Set_Background_Music)Address(bhs,"New_Set_Background_Music");
	Commands->Stop_Background_Music = (_Stop_Background_Music)Address(bhs,"New_Stop_Background_Music");
	Commands->Create_Sound = (_Create_Sound)Address(bhs,"New_Create_Sound");
	Commands->Create_2D_Sound = (_Create_2D_Sound)Address(bhs,"New_Create_2D_Sound");
	Commands->Create_2D_WAV_Sound = (_Create_2D_WAV_Sound)Address(bhs,"New_Create_2D_WAV_Sound");
	Commands->Create_3D_WAV_Sound_At_Bone = (_Create_3D_WAV_Sound_At_Bone)Address(bhs,"New_Create_3D_WAV_Sound_At_Bone");
	Commands->Create_3D_Sound_At_Bone = (_Create_3D_Sound_At_Bone)Address(bhs,"New_Create_3D_Sound_At_Bone");
	Commands->Play_Building_Announcement = (_Play_Building_Announcement)Address(bhs,"New_Play_Building_Announcement");
	Commands->Clear_Weapons = (_Clear_Weapons)Address(bhs,"New_Clear_Weapons");
	Commands->Enable_Vehicle_Transitions = (_Enable_Vehicle_Transitions)Address(bhs,"New_Enable_Vehicle_Transitions");
	Commands->Set_Player_Type = (_Set_Player_Type)Address(bhs,"New_Set_Player_Type");
	Commands->Set_Screen_Fade_Color = (_Set_Screen_Fade_Color)Address(bhs,"New_Set_Screen_Fade_Color");
	Commands->Set_Screen_Fade_Opacity = (_Set_Screen_Fade_Opacity)Address(bhs,"New_Set_Screen_Fade_Opacity");
	Commands->Set_Display_Color = (_Set_Display_Color)Address(bhs,"New_Set_Display_Color");
	Commands->Display_Text = (_Display_Text)Address(bhs,"New_Display_Text");
	Commands->Display_Float = (_Display_Float)Address(bhs,"New_Display_Float");
	Commands->Display_Int = (_Display_Int)Address(bhs,"New_Display_Int");
	Commands->Select_Weapon = (_Select_Weapon)Address(bhs,"New_Select_Weapon");
	Commands->Shake_Camera = (_Shake_Camera)Address(bhs,"New_Shake_Camera");
	Commands->Set_Model = (_Set_Model)Address(bhs,"New_Set_Model");
	Enable_Stealth_Player = (_Enable_Stealth_Player)Address(bhs,"New_Enable_Stealth_Player");
	Set_Fog_Enable_Player = (_Set_Fog_Enable_Player)Address(bhs,"New_Set_Fog_Enable_Player");
	Set_Fog_Range_Player = (_Set_Fog_Range_Player)Address(bhs,"New_Set_Fog_Range_Player");
	Set_Background_Music_Player = (_Set_Background_Music_Player)Address(bhs,"New_Set_Background_Music_Player");
	Fade_Background_Music_Player = (_Fade_Background_Music_Player)Address(bhs,"New_Fade_Background_Music_Player");
	Stop_Background_Music_Player = (_Stop_Background_Music_Player)Address(bhs,"New_Stop_Background_Music_Player");
	Enable_Radar_Player = (_Enable_Radar_Player)Address(bhs,"New_Enable_Radar_Player");
	Display_GDI_Player_Terminal_Player = (_Display_GDI_Player_Terminal_Player)Address(bhs,"New_Display_GDI_Player_Terminal_Player");
	Display_NOD_Player_Terminal_Player = (_Display_NOD_Player_Terminal_Player)Address(bhs,"New_Display_NOD_Player_Terminal_Player");
	Set_Screen_Fade_Color_Player = (_Set_Screen_Fade_Color_Player)Address(bhs,"New_Set_Screen_Fade_Color_Player");
	Set_Screen_Fade_Opacity_Player = (_Set_Screen_Fade_Opacity_Player)Address(bhs,"New_Set_Screen_Fade_Opacity_Player");
	Force_Camera_Look_Player = (_Force_Camera_Look_Player)Address(bhs,"New_Force_Camera_Look_Player");
	Enable_HUD_Player = (_Enable_HUD_Player)Address(bhs,"New_Enable_HUD_Player");
	Create_Sound_Player = (_Create_Sound_Player)Address(bhs,"New_Create_Sound_Player");
	Create_2D_Sound_Player = (_Create_2D_Sound_Player)Address(bhs,"New_Create_2D_Sound_Player");
	Create_2D_WAV_Sound_Player = (_Create_2D_WAV_Sound_Player)Address(bhs,"New_Create_2D_WAV_Sound_Player");
	Create_3D_WAV_Sound_At_Bone_Player = (_Create_3D_WAV_Sound_At_Bone_Player)Address(bhs,"New_Create_3D_WAV_Sound_At_Bone_Player");
	Create_3D_Sound_At_Bone_Player = (_Create_3D_Sound_At_Bone_Player)Address(bhs,"New_Create_3D_Sound_At_Bone_Player");
	Set_Display_Color_Player = (_Set_Display_Color_Player)Address(bhs,"New_Set_Display_Color_Player");
	Display_Text_Player = (_Display_Text_Player)Address(bhs,"New_Display_Text_Player");
	Display_Int_Player = (_Display_Int_Player)Address(bhs,"New_Display_Int_Player");
	Display_Float_Player = (_Display_Float_Player)Address(bhs,"New_Display_Float_Player");
	Commands->Set_Obj_Radar_Blip_Shape = (_Set_Obj_Radar_Blip_Shape)Address(bhs,"New_Set_Obj_Radar_Blip_Shape");
	Commands->Set_Obj_Radar_Blip_Color = (_Set_Obj_Radar_Blip_Color)Address(bhs,"New_Set_Obj_Radar_Blip_Color");
	Set_Obj_Radar_Blip_Shape_Player = (_Set_Obj_Radar_Blip_Shape_Player)Address(bhs,"New_Set_Obj_Radar_Blip_Shape_Player");
	Set_Obj_Radar_Blip_Color_Player = (_Set_Obj_Radar_Blip_Color_Player)Address(bhs,"New_Set_Obj_Radar_Blip_Color_Player");
	AddObjectCreateHook = (aoch)Address(bhs,"NewAddObjectCreateHook");
	RemoveObjectCreateHook = (roch)Address(bhs,"NewRemoveObjectCreateHook");
	AddKeyHook = (akh)Address(bhs,"AddKeyHook");
	RemoveKeyHook = (rkh)Address(bhs,"RemoveKeyHook");
	Set_Scope = (ss)Address(bhs,"Set_Scope");
	Set_HUD_Texture = (sh)Address(bhs,"Set_HUD_Texture");
	AddChatHook = (ach)Address(bhs,"AddChatHook");
	AddHostHook = (ahh)Address(bhs,"AddHostHook");
	AddLoadLevelHook = (allh)Address(bhs,"AddLoadLevelHook");
	AddGameOverHook = (allh)Address(bhs,"AddGameOverHook");
	AddPlayerJoinHook = (apjh)Address(bhs,"AddPlayerJoinHook");
	GetCurrentMusicTrack = (gcmt)Address(bhs,"GetCurrentMusicTrack");
	Set_Info_Texture = (sit)Address(bhs,"Set_Info_Texture");
	Clear_Info_Texture = (cit)Address(bhs,"Clear_Info_Texture");
	Set_Vehicle_Limit = (svl)Address(bhs,"Set_Vehicle_Limit");
	Get_Vehicle_Limit = (gvl)Address(bhs,"Get_Vehicle_Limit");
	Send_Message = (sm)Address(bhs,"Send_Message");
	Send_Message_Player = (smp)Address(bhs,"Send_Message_Player");
	Commands->Display_Health_Bar = (_Display_Health_Bar)Address(bhs,"New_Display_Health_Bar");
	AddVersionHook = (avh)Address(bhs,"AddVersionHook");
	Set_Wireframe_Mode = (sw)Address(bhs,"Set_Wireframe_Mode");
	Commands->Disable_All_Collisions = (_Disable_All_Collisions)Address(bhs,"New_Disable_All_Collisions");
	Commands->Disable_Physical_Collisions = (_Disable_Physical_Collisions)Address(bhs,"New_Disable_Physical_Collisions");
	Commands->Enable_Collisions = (_Enable_Collisions)Address(bhs,"New_Enable_Collisions");
	Load_New_HUD_INI = (lnhi)Address(bhs,"Load_New_HUD_INI");
	Remove_Weapon = (rw)Address(bhs,"Remove_Weapon");
	Update_PT_Data = (upd)Address(bhs,"Update_PT_Data");
	Change_Radar_Map = (crm)Address(bhs,"Change_Radar_Map");
	AddPowerupPurchaseHook = (aph)Address(bhs,"AddPowerupPurchaseHook");
	AddVehiclePurchaseHook = (aph)Address(bhs,"AddVehiclePurchaseHook");
	AddCharacterPurchaseHook = (aph)Address(bhs,"AddCharacterPurchaseHook");
	AddPowerupPurchaseMonHook = (apmh)Address(bhs,"AddPowerupPurchaseMonHook");
	AddVehiclePurchaseMonHook = (apmh)Address(bhs,"AddVehiclePurchaseMonHook");
	AddCharacterPurchaseMonHook = (apmh)Address(bhs,"AddCharacterPurchaseMonHook");
	RemovePowerupPurchaseHook = (rph)Address(bhs,"RemovePowerupPurchaseHook");
	RemoveVehiclePurchaseHook = (rph)Address(bhs,"RemoveVehiclePurchaseHook");
	RemoveCharacterPurchaseHook = (rph)Address(bhs,"RemoveCharacterPurchaseHook");
	RemovePowerupPurchaseMonHook = (rph)Address(bhs,"RemovePowerupPurchaseMonHook");
	RemoveVehiclePurchaseMonHook = (rph)Address(bhs,"RemoveVehiclePurchaseMonHook");
	RemoveCharacterPurchaseMonHook = (rph)Address(bhs,"RemoveCharacterPurchaseMonHook");
	Get_Build_Time_Multiplier = (gbm)Address(bhs,"Get_Build_Time_Multiplier");
	Set_Currently_Building = (scb)Address(bhs,"Set_Currently_Building");
	Is_Currently_Building = (icb)Address(bhs,"Is_Currently_Building");
	AddConsoleOutputHook = (acoh)Address(bhs,"AddConsoleOutputHook");
	AddCRCHook = (acrch)Address(bhs,"AddCRCHook");
	AddDataHook = (adh)Address(bhs,"AddDataHook");
	Set_Reticle_Texture1 = (srt)Address(bhs,"Set_Reticle_Texture1");
	Set_Reticle_Texture2 = (srt)Address(bhs,"Set_Reticle_Texture2");
	Set_Fog_Color = (sfc)Address(bhs,"New_Set_Fog_Color");
	Set_Fog_Color_Player = (sfcp)Address(bhs,"Set_Fog_Color_Player");
	Set_Fog_Mode = (sfm)Address(bhs,"New_Set_Fog_Mode");
	Set_Fog_Mode_Player = (sfmp)Address(bhs,"Set_Fog_Mode_Player");
	Set_Shader_Number = (ssn)Address(bhs,"Set_Shader_Number");
	Send_HUD_Number = (shn)Address(bhs,"SendHUDNumber");
	Set_Fog_Density = (sfd)Address(bhs,"New_Set_Fog_Density");
	Set_Fog_Density_Player = (sfdp)Address(bhs,"Set_Fog_Density_Player");
	Change_Time_Remaining = (ctr)Address(bhs,"New_Change_Time_Remaining");
	Change_Time_Limit = (ctl)Address(bhs,"New_Change_Time_Limit");
	Display_GDI_Sidebar = (dps)Address(bhs,"Display_GDI_Sidebar");
	Display_NOD_Sidebar = (dps)Address(bhs,"Display_NOD_Sidebar");
	Display_Security_Dialog = (dps)Address(bhs,"Display_Security_Dialog");
	AddPlayerLeaveHook = (aplh)Address(bhs,"AddPlayerLeaveHook");
	GetExplosionObj = (geo)Address(bhs,"GetExplosionObj");
	GetBHSVersion = (gbhsv)Address(bhs,"GetBHSVersion");
	Commands->Control_Enable = (_Control_Enable)Address(bhs,"New_Control_Enable");
	Set_Shader_Number_Vector = (ssn2)Address(bhs,"Set_Shader_Number_Vector");
	AddShaderNotify = (asn)Address(bhs,"AddShaderNotify");
	RemoveShaderNotify = (rsn)Address(bhs,"RemoveShaderNotify");
	if (GetBHSVersion() != BHS_VERSION)
	{
		Console_Output("Fatal Error: scripts.dll requires a bhs.dll version of %f. You are running %f.",BHS_VERSION,GetBHSVersion());
		return false;
	}
	LoadScopes();
	YaRR_Load();

	return (SetScriptCommands) (ScriptCommands);
}

void SCRIPTSAPI Set_Request_Destroy_Func(rdf Func)
{
	ScriptImpClass::Set_Request_Destroy_Func(Func);
	(SetRequestDestroyFunc) (Func);
}


} //extern "C"
