/* Renegade Scripts.dll
	Base 3D drawing related engine classes and calls
	Copyright 2007 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_common.h"
#include "engine_vector.h"
#include "engine_threading.h"
#include "engine_string.h"
#include "engine_io.h"
#include "engine_math.h"
#include "engine_d3d.h"
#include "engine_def.h"
#include "engine_net.h"
#include "engine_obj.h"
#include "engine_3dre.h"
typedef void *(*Get3DFont) (const char *texture);

FontCharsClass **Fonts;
RectClass *ScreenResolution;
Get3DFont Get_Font;
void *AssetManager;
void *Render2DClassCreate;
void *Render2DClassReset;
void *Render2DClassRender;
void *Render2DClassSetRange;
void *Render2DClassSetTexture;
void *Render2DClassAddQuad2;
void *Render2DClassAddQuad;
void *Render2DClassAddLine;
void *Render2DTextClassDestroy;
void *Render2DClassDestroy;
void *Render2DTextClassReset;
void *Draw2DText;
void *Render2DTextClassCreate;
void *Render2DSentenceClassCreate;
void *Render2DSentenceReset;
void *Render2DSentenceRender;
void *Render2DSentenceBuild;
void *Render2DSentenceSetLocation;
void *Render2DSentenceClassDestroy;
void *Render2DDrawSentence;
void *Render2DSentenceSetFont;
void *Get_Texture;
void *Release_Texture;
myIDirect3DDevice8 **Direct3DDevice8;
IDirect3DDevice9 *Direct3DDevice;
DX8Caps *CurrentCaps;
void *TextureInit;
ReferenceableClass<ScriptableGameObj> **TheStar;
unsigned int *CompassPos;
GameObject **BracketObj;
void *SecondsToHMS;
float *RadarIntensity;
unsigned int *SyncTime;
void *MissingTexture;
bool *IsNighttime;
bool *UVBiased;
void *CreateDX8Surface;

myIDirect3DSurface8 THUNK *Create_DX8_Surface(unsigned int width,unsigned int height,WW3DFormat format)
IMPLEMENT_THUNK_RETURN(CreateDX8Surface)
void THUNK Seconds_To_Hms(float secs, int &hours, int &minutes, int &seconds)
IMPLEMENT_THUNK(SecondsToHMS)
void THUNK TextureClass::Init()
IMPLEMENT_THUNK(TextureInit)
bool THUNK TextureClass::Is_Missing_Texture()
IMPLEMENT_THUNK_RETURN(MissingTexture)

void InitEngine3DRE(unsigned int exe)
{
	switch(exe)
	{
		case 0: //game.exe
			CreateDX8Surface = (void *)0x0054B1E0;
			UVBiased = (bool *)0x00830487;
			IsNighttime = (bool *)0x0085DB64;
			SyncTime = (unsigned int *)0x00830474;
			TextureInit = (void *)0x005503E0;
			Direct3DDevice8 = (myIDirect3DDevice8 **)0x00831474;
			Direct3DDevice = (*Direct3DDevice8)->device9;
			Get_Texture = (void *)0x00543920;
			Release_Texture = (void *)0x00543F80;
			Fonts = (FontCharsClass **)0x0082FE20;
			Render2DSentenceClassCreate = (void *)0x0052EA00;
			Render2DSentenceClassDestroy = (void *)0x0052EB40;
			Render2DSentenceRender = (void *)0x0052EE10;
			Render2DSentenceReset = (void *)0x0052ECB0;
			Render2DSentenceBuild = (void *)0x0052FFF0;
			Render2DSentenceSetLocation = (void *)0x0052EEB0;
			Render2DDrawSentence = (void *)0x0052F7F0;
			Render2DSentenceSetFont = (void *)0x0052EC40;
			AssetManager = (void *)0x00830720;
			Get_Font = (Get3DFont)0x005443E0;
			Render2DTextClassCreate = (void *)0x00535D80;
			Render2DTextClassDestroy = (void *)0x00535EF0;
			Draw2DText = (void *)0x005366C0;
			Render2DClassCreate = (void *)0x00532590;
			Render2DClassDestroy = (void *)0x00532810;
			ScreenResolution = (RectClass *)0x008305C0;
			Render2DClassRender = (void *)0x00534FC0;
			Render2DClassSetRange = (void *)0x00532B20;
			Render2DClassSetTexture = (void *)0x00532A10;
			Render2DClassAddQuad2 = (void *)0x00533D50;
			Render2DClassAddQuad = (void *)0x005340C0;
			Render2DClassAddLine = (void *)0x00534470;
			Render2DClassReset = (void *)0x00532960;
			Render2DTextClassReset = (void *)0x00536140;
			CurrentCaps = *(DX8Caps **)0x008313E0;
			TheStar = (ReferenceableClass<ScriptableGameObj> **)0x00855E48;
			CompassPos = (unsigned int *)0x00856F38;
			BracketObj = (GameObject **)0x00856FB4;
			SecondsToHMS = (void *)0x0066E340;
			RadarIntensity = (float *)0x00856E9C;
			MissingTexture = (void *)0x005504E0;
			break;
		default:
			CreateDX8Surface = (void *)0x0;
			UVBiased = (bool *)0x0;
			IsNighttime = (bool *)0x0;
			SyncTime = (unsigned int *)0x0;
			TextureInit = (void *)0x0;
			Direct3DDevice8 = (myIDirect3DDevice8 **)0x0;
			Direct3DDevice = (IDirect3DDevice9 *)0x0;
			Get_Texture = (void *)0x0;
			Release_Texture = (void *)0x0;
			Fonts = (FontCharsClass **)0x0;
			Render2DSentenceClassCreate = (void *)0x0;
			Render2DSentenceClassDestroy = (void *)0x0;
			Render2DSentenceRender = (void *)0x0;
			Render2DSentenceReset = (void *)0x0;
			Render2DSentenceBuild = (void *)0x0;
			Render2DSentenceSetLocation = (void *)0x0;
			Render2DDrawSentence = (void *)0x0;
			Render2DSentenceSetFont = (void *)0x0;
			AssetManager = (void *)0x0;
			Get_Font = (Get3DFont)0x0;
			Render2DTextClassCreate = (void *)0x0;
			Render2DTextClassDestroy = (void *)0x0;
			Draw2DText = (void *)0x0;
			Render2DClassCreate = (void *)0x0;
			Render2DClassDestroy = (void *)0x0;
			ScreenResolution = (RectClass *)0x0;
			Render2DClassRender = (void *)0x0;
			Render2DClassSetRange = (void *)0x0;
			Render2DClassSetTexture = (void *)0x0;
			Render2DClassAddQuad2 = (void *)0x0;
			Render2DClassAddQuad = (void *)0x0;
			Render2DClassAddLine = (void *)0x0;
			Render2DClassReset = (void *)0x0;
			Render2DTextClassReset = (void *)0x0;
			CurrentCaps = (DX8Caps *)0x0;
			TheStar = (ReferenceableClass<ScriptableGameObj> **)0x0;
			CompassPos = (unsigned int *)0x0;
			BracketObj = (GameObject **)0x0;
			SecondsToHMS = (void *)0x0;
			RadarIntensity = (float *)0x0;
			MissingTexture = (void *)0x0;
			break;
	}
}

void Set_DX8_Transform(D3DTRANSFORMSTATETYPE transform,Matrix4& m)
{
	switch ((unsigned int)transform)
	{
	case D3DTS_VIEW:
		//view goes here
		break;
	case D3DTS_PROJECTION:
		//projection goes here
		break;
	case D3DTS_WORLD:
		//world goes here
		break;
	default:
		Direct3DDevice->SetTransform(transform,(D3DMATRIX *)&m);
		break;
	}
}

void Get_DX8_Transform(D3DTRANSFORMSTATETYPE transform,Matrix4& m)
{
	switch ((unsigned int)transform)
	{
	case D3DTS_VIEW:
		//view goes here
		break;
	case D3DTS_PROJECTION:
		//projection goes here
		break;
	case D3DTS_WORLD:
		//world goes here
		break;
	default:
		Direct3DDevice->GetTransform(transform,(D3DMATRIX *)&m);
		break;
	}
}

Render2DSentenceClass *CreateRender2DSentenceClass(unsigned int font)
{
	Render2DSentenceClass *render2D = new Render2DSentenceClass();
	FontCharsClass *f = Fonts[font];
	render2D->Set_Font(f);
	return render2D;
}

void Render2DSentenceDrawSentence(Render2DSentenceClass *r,const wchar_t *sentence,Vector2 *position,unsigned long color)
{
	r->Reset();
	r->Build_Sentence(sentence);
	r->Set_Location(*position);
	r->Draw_Sentence(color);
	r->Render();
}

Render2DTextClass *CreateRender2DTextClass(const char *texture)
{
	Font3DInstanceClass *font;
	_asm {
		mov ecx, AssetManager
		mov eax, texture
		push eax
		mov ebx, Get_Font
		call ebx
		mov font, eax;
	}
#ifdef DEBUG
	memoryPassthrough = true;
#endif
	Render2DTextClass *render2D = new Render2DTextClass(font);
	render2D->Set_Coordinate_Range(*ScreenResolution);
#ifdef DEBUG
	memoryPassthrough = false;
#endif
	return render2D;
}

Render2DClass::Render2DClass(TextureClass *texture)
{
	_asm {
		mov dword ptr [ebp-4], -1
		mov ecx, this
		mov eax, [ebp-0x0C]
		mov fs:0, eax
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DClassCreate
		jmp eax
	}
}

void Render2DClass::Update_Bias()
{
	BiasedCoordinateOffset.X = CoordinateOffset.X;
	BiasedCoordinateOffset.Y = CoordinateOffset.Y;
	if (*UVBiased)
	{
		float f = (float)(-0.5 / ((ScreenResolution->Right - ScreenResolution->Left) * 0.5));
		float f2 = (float)(-0.5 / ((ScreenResolution->Bottom - ScreenResolution->Top) * -0.5));
		BiasedCoordinateOffset.X += f;
		BiasedCoordinateOffset.Y += f2;
	}
}

void Render2DClass::Reset()
{
	Vertices.Reset_Active();
	UVCoordinates.Reset_Active();
	Colors.Reset_Active();
	Indices.Reset_Active();
	Update_Bias();
}

void Render2DClass::Set_Coordinate_Range(RectClass const &r)
{
	CoordinateScale.X = (float)(2.0 / (r.Right - r.Left));
	CoordinateScale.Y = (float)(-2.0 / (r.Bottom - r.Top));
	CoordinateOffset.X = (float)((-(CoordinateScale.X * r.Left)) - 1.0);
	CoordinateOffset.Y = (float)(1.0 - (r.Top * CoordinateScale.Y));
	Update_Bias();
}

void Render2DTextClass::Reset()
{
	Render2DClass::Reset();
	Cursor.X = Location.X;
	Cursor.Y = Location.Y;
	WrapWidth = 0;
	DrawExtents.Left = 0;
	DrawExtents.Top = 0;
	DrawExtents.Right = 0;
	DrawExtents.Bottom = 0;
	TotalExtents.Left = 0;
	TotalExtents.Top = 0;
	TotalExtents.Right = 0;
	TotalExtents.Bottom = 0;
	ClipRect.Left = 0;
	ClipRect.Right = 0;
	ClipRect.Top = 0;
	ClipRect.Bottom = 0;
	IsClippedEnabled = false;
}

SurfaceClass::SurfaceClass(unsigned int width,unsigned int height,WW3DFormat format)
{
	Add_Ref();
	SurfaceFormat = format;
	D3DSurface = Create_DX8_Surface(width,height,format);
}

void SurfaceClass::Get_Description(SurfaceDescription& surface_desc)
{
	D3DSURFACE_DESC d3d_desc;
	D3DSurface->surface9->GetDesc(&d3d_desc);
	surface_desc.Height = d3d_desc.Height;
	surface_desc.Width = d3d_desc.Width;
	surface_desc.Format = SurfaceFormat; //should really use D3DFormat_To_WW3DFormat here
}

void *SurfaceClass::Lock(int* pitch)
{
	D3DLOCKED_RECT r;
	r.Pitch = 0;
	r.pBits = 0;
	D3DSurface->surface9->LockRect(&r,NULL,0);
	*pitch = r.Pitch;
	return r.pBits;
}

void *SurfaceClass::Lock_ReadOnly(int* pitch)
{
	D3DLOCKED_RECT r;
	r.Pitch = 0;
	r.pBits = 0;
	D3DSurface->surface9->LockRect(&r,NULL,D3DLOCK_READONLY);
	*pitch = r.Pitch;
	return r.pBits;
}

void SurfaceClass::Unlock()
{
	D3DSurface->surface9->UnlockRect();
}

IDirect3DSurface9 *SurfaceClass::Peek_D3D_Surface()
{
	return D3DSurface->surface9;
}

WW3DFormat SurfaceClass::Get_Surface_Format()
{
	return SurfaceFormat;
}

SurfaceClass::~SurfaceClass()
{
	D3DSurface->Release();
	D3DSurface = 0;
}

/*void THUNK Render2DClass::Reset()
IMPLEMENT_THUNK(Render2DClassReset)*/
void THUNK Render2DClass::Render()
IMPLEMENT_THUNK(Render2DClassRender)
/*void THUNK Render2DClass::Set_Coordinate_Range(RectClass const &r)
IMPLEMENT_THUNK(Render2DClassSetRange)*/
void THUNK Render2DClass::Set_Texture(char const *texture)
IMPLEMENT_THUNK(Render2DClassSetTexture)
void THUNK Render2DClass::Add_Quad(RectClass const &screen, RectClass const &uv, unsigned long color)
IMPLEMENT_THUNK(Render2DClassAddQuad2)
void THUNK Render2DClass::Add_Quad(RectClass const &screen, unsigned long color)
IMPLEMENT_THUNK(Render2DClassAddQuad)
void THUNK Render2DClass::Add_Line(Vector2 const &start, Vector2 const &end, float width, unsigned long color)
IMPLEMENT_THUNK(Render2DClassAddLine)
Render2DTextClass::~Render2DTextClass()
{
	_asm {
		mov ecx, this
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DTextClassDestroy
		jmp eax
	}
}
Render2DClass::~Render2DClass()
{
	_asm {
		mov ecx, this
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DClassDestroy
		jmp eax
	}
}
/*void THUNK Render2DTextClass::Reset()
IMPLEMENT_THUNK(Render2DTextClassReset)*/
void THUNK Render2DTextClass::Draw_Text(char const *, unsigned long)
IMPLEMENT_THUNK(Draw2DText)
Render2DTextClass::Render2DTextClass(Font3DInstanceClass *)
{
	_asm {
		mov ecx, this
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DTextClassCreate
		jmp eax
	}
}
Render2DSentenceClass::Render2DSentenceClass()
{
	_asm {
		mov ecx, this
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DSentenceClassCreate
		jmp eax
	}
}
void THUNK Render2DSentenceClass::Reset()
IMPLEMENT_THUNK(Render2DSentenceReset)
void THUNK Render2DSentenceClass::Render()
IMPLEMENT_THUNK(Render2DSentenceRender)
void THUNK Render2DSentenceClass::Build_Sentence(wchar_t const *sentence)
IMPLEMENT_THUNK(Render2DSentenceBuild)
void THUNK Render2DSentenceClass::Set_Location(Vector2 const &location)
IMPLEMENT_THUNK(Render2DSentenceSetLocation)
Render2DSentenceClass::~Render2DSentenceClass()
{
	_asm {
		mov ecx, this
		mov	esp, ebp
		pop	ebp
		mov eax, Render2DSentenceClassDestroy
		jmp eax
	}
}
void THUNK Render2DSentenceClass::Draw_Sentence(unsigned long color)
IMPLEMENT_THUNK(Render2DDrawSentence)
void THUNK Render2DSentenceClass::Set_Font(FontCharsClass *)
IMPLEMENT_THUNK(Render2DSentenceSetFont)
Render2DClass *CreateRender2DClass()
{
#ifdef DEBUG
	memoryPassthrough = true;
#endif
	Render2DClass *render2D = new Render2DClass(0);
	render2D->Set_Coordinate_Range(*ScreenResolution);
#ifdef DEBUG
	memoryPassthrough = false;
#endif
	return render2D;
}

TextureClass __declspec(naked) *_stdcall Load_Texture(const char *path,MipCountType mip,WW3DFormat format, bool IsCompressionAllowed)
{
	_asm {
		mov ecx, AssetManager
		mov ecx, [ecx]
		mov eax, Get_Texture
		jmp eax
	}
}

void __declspec(naked) _stdcall Free_Texture(TextureClass *texture)
{
	_asm {
		mov ecx, AssetManager
		mov eax, Release_Texture
		jmp eax
	}
}

#if DEBUG
int DebugEventStart(D3DCOLOR color, wchar_t *message,...)
{
	va_list args;
	wchar_t buffer[256];
	
	va_start(args,message);
	vswprintf(buffer,sizeof(buffer)-1,message,args);
	va_end(args);
	return D3DPERF_BeginEvent(color,buffer);
}
int DebugEventEnd()
{
	return D3DPERF_EndEvent();
}
void DebugEventMarker(D3DCOLOR color, wchar_t *message,...)
{
	va_list args;
	wchar_t buffer[256];
	
	va_start(args,message);
	vswprintf(buffer,sizeof(buffer)-1,message,args);
	va_end(args);
	D3DPERF_SetMarker(color,buffer);
}
int DebugGetStatus()
{
	return D3DPERF_GetStatus();
}
#else
int DebugEventStart(D3DCOLOR color, wchar_t *message,...)
{
	return 0;
}
int DebugEventEnd()
{
	return 0;
}
void DebugEventMarker(D3DCOLOR color, wchar_t *message,...)
{
	__noop();
}
int DebugGetStatus()
{
	return 0;
}
#endif
