#include "include.h"
#include <crtdbg.h>
#include <eh.h>
#include <process.h>
//#include <objbase.h>

/*** defines ***/
//#define AUTHORISATION_CHECK

/*** globals ***/

defglobal HINSTANCE GhInstance;
defglobal char* GpCmdLine;

/*** locals ***/
DefLocalString(Version,"Prerelease " __DATE__);
//DefLocalString(VersionRegisteredTo,"Internal development only");
DefLocalString(VersionRegisteredTo,"Public Release");
//DefLocalString(VersionRegisteredTo,"Play Guide");
//DefLocalString(VersionRegisteredTo,"3 Level Magazine Preview");
//DefLocalString(VersionRegisteredTo,"Jeremy Smith, for Eidos meeting");
//DefLocalString(VersionRegisteredTo,"Mike Schmitt, pre-ECTS version");
//DefLocalString(VersionRegisteredTo,"Eidos USA, for buyers");
//DefLocalString(VersionRegisteredTo,"ECTS Demo");
DefLocalString(ReleaseCode,"0000");
//DefLocalString(ReleaseCode,"0001");
//DefLocalString(ReleaseCode,"0003");
DefLocalString(CopyrightMessage,"Copyright (c) 1997 Core Design Ltd.");
#ifdef MAIL_OK
DefLocalString(AuthorisationEmailAddress,"AndrewH@Core-Design.com");
DefLocalString(AuthorisationSubject,"Authorisation request for TR2 Beta");
DefLocalString(VersionExpiredEmailAddress,"AndrewH@Core-Design.com");
DefLocalString(VersionExpiredSubject,"Version Expired");
#endif

local char* LaszCoderNames[]=
	{
	"Andrew",
	"Gavin",
	"Jason",
	"Richard",
	};

#ifdef AUTHORISATION_CHECK
local uint8 LabNetAddr[6];
local char LacKey[32];
local char LacBody[256];
#endif

/*** Exception class ***/
class SE_Exception
	{
private:
    unsigned int nSE;
protected:
public:
    SE_Exception() {}
    SE_Exception(SE_Exception&) {}
	SE_Exception(unsigned int n) : nSE(n) {}
    ~SE_Exception() {}
    unsigned int GetCode() {return nSE;}
	};

/********************************** FUNCTION CODE ****************************/

bool CD_Init(void);
void CD_Cleanup(void);

local int Init(bool tIsSetup)
	{
	Log(LT_Enter,"Init");
#ifdef _DEBUG
	_CrtSetReportMode(_CRT_WARN,_CRTDBG_MODE_DEBUG);
	int temp=_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
	temp|=_CRTDBG_LEAK_CHECK_DF;
	_CrtSetDbgFlag(temp);
#endif

	if (!tIsSetup)
		if (!CD_Init())
			return 2;

	UT_InitAccurateTimer();
	if (DD_Init()	&&
		D3D_Init()	&&
		DX_Init()	&&
		TX_Init()	&&
		DS_Init()	&&
		DI_Init()	&&
		TIME_Init() &&
		HWR_Init()	&&
		BGND_Init())
		return 1;
	return 0;
	}

local void Cleanup()
	{
	Log(LT_Enter,"Cleanup");
	DI_Cleanup();
	DS_Cleanup();
	TX_Cleanup();
	DX_Cleanup();
	D3D_Cleanup();
	DD_Cleanup();

	CD_Cleanup();
	}

local int Start()
	{
	Log(LT_Enter,"Start");
	try
		{
		DD_Start();
		DX_Start();
		DS_Start();
		DI_Start();
		}
	catch (InitResult nERR)
		{
		return nERR;
		}
	return INIT_OK;
	}

local void Finish()
	{
	Log(LT_Enter,"Finish");
	DI_Finish();
	DS_Finish();	
	DX_Finish();
	DD_Finish();
	DD_HideWindow();
	if (exit_message[0])	// Print an error message if one exists
		{
		Log(exit_message);
		MessageBox(NULL,exit_message,NULL,MB_OK|MB_ICONEXCLAMATION);
		}
	}

local void _cdecl SETransFunc(unsigned int u,_EXCEPTION_POINTERS* pExp)
	{
	EXCEPTION_RECORD* pER=pExp->ExceptionRecord;
    Log("Exception: %x at %x",u,pER->ExceptionAddress);
	throw SE_Exception(u);
	}

#ifdef AUTHORISATION_CHECK
local bool IsAuthorisationKeyValid()
	{
	static uint8 bEncStr[6]={'L','A','M','E','R','!'};
	uint8 bEncrypted[6];
	char acBuffer[16];
	for (int i=0;i<6;++i)
		bEncrypted[i]=LabNetAddr[i]^bEncStr[i];
	sprintf(acBuffer,"%x%x%x%x%x%x",bEncrypted[0],bEncrypted[1],bEncrypted[2],bEncrypted[3],bEncrypted[4],bEncrypted[5]);
	return strcmp(acBuffer,LacKey)==0;
	}

local void UpdateBody(HWND hWnd)
	{
	LacBody[0]='P';
	LacBody[1]='W';
	LacBody[2]=':';
	LacBody[3]=' ';
	GetDlgItemText(hWnd,IDC_PASSWORD,LacBody+4,32);
	int nLen=strlen(LacBody);
	sprintf(LacBody+nLen,"\r\nID: %02x%02x%02x%02x%02x%02x",LabNetAddr[0],LabNetAddr[1],LabNetAddr[2],LabNetAddr[3],LabNetAddr[4],LabNetAddr[5]);
	SetDlgItemText(hWnd,IDC_BODY,LacBody);
	}

local BOOL WINAPI AuthorisationDlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
	{
	switch (msg)
		{
		case WM_INITDIALOG:
			LacKey[0]=0;
			SetDlgItemText(hWnd,IDC_KEY,"");
			SetDlgItemText(hWnd,IDC_PASSWORD,"");
			SetDlgItemText(hWnd,IDC_TO,LocalString(AuthorisationEmailAddress));
			SetDlgItemText(hWnd,IDC_SUBJECT,LocalString(AuthorisationSubject));
			EnableWindow(GetDlgItem(hWnd,IDOK),false);
			UT_CenterWindow(hWnd);
			UpdateBody(hWnd);
			return true;

		case WM_COMMAND:
			switch (LOWORD(wParam))
				{
				case IDOK:
					EndDialog(hWnd,1);
					return true;

				case IDCANCEL:
					EndDialog(hWnd,0);
					return true;

				case IDC_KEY:
					if (HIWORD(wParam)==EN_CHANGE)
						{
						GetWindowText((HWND)lParam,LacKey,16);
						EnableWindow(GetDlgItem(hWnd,IDOK),IsAuthorisationKeyValid());
						}
					break;

				case IDC_PASSWORD:
					if (HIWORD(wParam)==EN_CHANGE)
						UpdateBody(hWnd);
					break;

				case IDC_APPLY:
					EndDialog(hWnd,2);
					return true;

				default:
					break;
				}
			break;

		default:
			break;
		}
	return false;
	}
#endif


#ifdef DEMO
local BOOL WINAPI DemoDlgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
	{
	static HWND hDemoPicCtrl;

	switch (msg)
		{
		case WM_INITDIALOG:
			UT_CenterWindow(hWnd);
#ifdef PCGAMER
			hDemoPicCtrl=GetDlgItem(hWnd,IDC_PCGAMER);
#else
			hDemoPicCtrl=GetDlgItem(hWnd,IDC_LARAPIC);
#endif
			return true;

		case WM_COMMAND:
			switch (LOWORD(wParam))
				{
				case IDOK:
					EndDialog(hWnd,1);
					return true;

				case IDCANCEL:
					EndDialog(hWnd,0);
					return true;

				default:
					break;
				}
			break;

		default:
			break;
		}
	DW_HandlePaletteChanges(hDemoPicCtrl,msg,wParam);
	return false;
	}

void DoDemoDialog(void)
	{
	DW_RegisterWindowClass();
#ifdef PCGAMER
	DialogBox(GhInstance,MAKEINTRESOURCE(IDD_PCGAMER),HWND_DESKTOP,(DLGPROC)DemoDlgProc);
#else
	DialogBox(GhInstance,MAKEINTRESOURCE(IDD_DEMO),HWND_DESKTOP,(DLGPROC)DemoDlgProc);
#endif
	}
#endif

void Complain(int nResult)
	{
	char acBuffer[128];
	sprintf(acBuffer,"Render init failed with \"%s\"",ERR_Describe_Init(nResult));
	UT_ErrorBox(acBuffer);
	}

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE previnst, LPSTR cmdline, int cmdshow)
	{
	GhInstance=hinstance;							// save some handy global values
	GpCmdLine=cmdline;

	HWND hPrevWnd=DD_FindWindow();
	if (hPrevWnd)
		{
		HWND hActiveWnd=SE_FindWindow();
		if (!hActiveWnd)
			hActiveWnd=hPrevWnd;
		SetForegroundWindow(hActiveWnd);
		return 0;
		}

	InitLog("Tomb Raider II");						// start Dude's handy debug log prog (#ifdef DBLOG)
	Log(LT_Version,"Version: %s",LocalString(Version));
	Log(LT_Version,"Registered to: %s",LocalString(VersionRegisteredTo));
	Log(LT_Version,"Release code: %s",LocalString(ReleaseCode));
	Log(LT_Version,"Built by: %s",LaszCoderNames[CODER]);
	Log(LT_Version,LocalString(CopyrightMessage));

#ifdef GAMEDEBUG
	int n;
	if (UT_GetArg("cut",n))
		SetCutscene(n);
	if (UT_GetArg("track",n))
		SetCutsceneTrack(n);
	if (UT_GetArg("angle",n))
		SetCutsceneAngle(n);
#endif

#ifdef PCGAMER
	DoDemoDialog();
#endif

	try
		{
		_set_se_translator(SETransFunc);
		bool tExit=FALSE;
		bool tIsSetup=UT_FindArg("setup");
		int nInitVal=Init(tIsSetup);
		if (nInitVal==1)									// init adapter lists and stuff
			{
			int nErr=SE_ReadAppSettings(G_AppSettings);		// read registry settings

			bool tFirstTime=(nErr==SE_RS_NEW);				// tNewSettings is true if no registry key existed
			if (nErr==SE_RS_ERROR)							// exit if settings couldn't be read at all
				tExit=true;
			else if (tFirstTime || tIsSetup)				// if no previous settings or we got "setup" arg...
				{											// time for Dude's handy setup dialog!
				if (SE_DoPropertySheet(NULL,tFirstTime))	// do it, setting defaults if this is the first time
					{										// if user pressed OK...
					SE_WriteAppSettings(G_AppSettings);		// save the new settings in the registry
//					if (!tFirstTime)						// if it's not the first time...
					if (tIsSetup)							// if we're only doing a setup...
						tExit=true;							// setup option doesn't go into game, just exits
					}
				else
					tExit=true;								// if user pressed Cancel then don't run game
				}

			while (!tExit)
				{
				int nResult=Start();							// try to init DirectDraw, Direct3D, DirectSound, DirectInput etc
				if (nResult==INIT_OK)							// if it all initialised ok...
					{
					GtWindowClosed=game_closedown=false;
					GameMain();									// play the game
					Finish();									// free DirectX stuff
					SE_WriteAppSettings(G_AppSettings);			// save any options that have changed, like 320x200/640x480
					tExit=true;									// and exit
					}
				else
					{
					Log(LT_Error,"Start: %s",ERR_Describe_Init(nResult));
					Finish();									// if it failed to initialise...
					Complain(nResult);							// bring up a message box saying what went wrong
					if (SE_DoPropertySheet(NULL,false))			// allow user to change settings
						SE_WriteAppSettings(G_AppSettings);		// if they accepted them... save them and loop round to try them
					else
						tExit=true;								// else exit and cancel settings
					}
				}
			} 
		else if (nInitVal==0)
			UT_ErrorBox(IDS_INITIALISE_FAILED);					// no DirectX 5 ???
		}
	catch (char* szError)
		{
		Log("Caught string:%s",szError);
		Finish();
		UT_ErrorBox(szError);
		}
	catch (SE_Exception se)
		{
		Log("Exception caught, cleaning up");
		Finish();
		Cleanup();
		_cexit();	// destructors etc
		abort();	// allow a bit of debugging
		}

#if defined(DEMO) && !defined(PCGAMER)
	DoDemoDialog();
#endif

	Cleanup();
	EndLog();
	return GnAppReturnValue;
	}

// a bit dodgy... but usually works OK
extern "C" void S_ExitSystem(char *message)
	{
	Log(LT_Enter,"S_ExitSystem");
	ShutdownGame();
	strcpy(exit_message,message);
	Finish();
	Cleanup();
	EndLog();
	exit(1);
	}


/***************************************************************************
 *				Save Current Screen To tombXXXX.pcx
 *				  where XX is incrementing number from 01
 **************************************************************************/
int Compress(char *ptr, int w, int h , uint8 *palptr, char **compressed);
int encode_PCX_line( char *inbuff, int scanlen, char *fptr );
int encputPCX( char byt, char count, char *fptr );

void SaveScreen(void)
{
	static savenum = 0;
	int width, height;
	char *ptr, *pcx, sname[20];
	DDSURFACEDESC desc;
	HRESULT result;
	HANDLE file;
	unsigned long read;
	long length;
	IDirectDrawSurface3* pSurf;

	/* Hardware or software picture? */
	if (G_AppSettings.nRenderMode==RENDERER_INTERNAL)
		pSurf = G_RenderBuffer;
	else
		pSurf = G_FrontBuffer;

	desc.dwSize = sizeof(desc);
	do
		result = pSurf->Lock(NULL, &desc, 0, NULL);
	while (result == DDERR_WASSTILLDRAWING);

	if (result == DDERR_SURFACELOST)
	{
		pSurf->Restore();
		return;
	}
	else if (SUCCEEDED(result))
		ptr = (char *)desc.lpSurface;
	else
		{
		DX_TRY(result);
		return;
		}
		//DDFailure("CompPCX,Lock", result);

	width = desc.dwWidth;
	height = desc.dwHeight;

  	length = Compress(ptr, width, height, game_palette, &pcx);

	pSurf->Unlock(&desc);

	if (length)
   {
		// Name of file
		if (++savenum > 9999)
			savenum = 1;
		wsprintf(sname, "tomb%04d.pcx", savenum);

		file = CreateFile(sname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		WriteFile(file, pcx, length, &read, NULL);
		CloseHandle(file);

  		GlobalFree((HGLOBAL)pcx);
  	}
}

/**************************************************************************
 *		NOTE only handles 256 color byte packed filetypes
 *			Also Compresses PCX to A Buffer Which You
 *				Must Free!!! ie free(pcx_ptr);
 *************************************************************************/
int Compress(char *ptr, int w, int h , uint8 *palptr, char **compressed)
{
	int i, sz, length;
	char *tmp, *mptr;
	PCX_HEADER *pcx;

	sz = (w*h*2) + sizeof(PCX_HEADER) + (256*3);

	if( (mptr = (char *)GlobalAlloc(GMEM_FIXED, sz) ) == NULL)
		return(0);

	*compressed = tmp = mptr;

	pcx = (PCX_HEADER *)tmp;

	pcx->zsoft_flag = 10;
	pcx->version = 5;           // Insert PCX header
	pcx->bits = 8;
	pcx->encoding = 1;
	pcx->planes = 1;

	pcx->xmax = w-1;
	pcx->xmin = 0;
	pcx->ymax = h-1;
	pcx->ymin = 0;

	pcx->h_res = w;
	pcx->v_res = h;
	pcx->bytes_per_line = w;

	tmp += sizeof(PCX_HEADER);

	for (i=0; i<h; i++, ptr+=w)				// encode each scan line
		tmp += encode_PCX_line(ptr, w, tmp);

	*(tmp++) = 12;								// pal header byte
	for(i=0; i<256*3; i++)
		*(tmp++) = *(palptr++);           	// Store Palette Components
	length = (int)tmp - (int)(*compressed);

	return(length);
}

/****************************************************************************
 *		Encode One Scan Line Of PCX graphics 256-color format only...
 *			returns number bytes written to fptr..
 *				or 0 if an error has occured..
 ***************************************************************************/
int encode_PCX_line( char *inbuff, int scanlen, char *fptr )
{
	char last, current, runcount;
	int srcindex,i,total;

	total = 0;
	runcount = 1;
	last = *(inbuff);

	for ( srcindex=1; srcindex<scanlen; srcindex++ )
	{
		current = *(++inbuff);
		if ( current==last ) 				// there is a run of pixels
		{
			runcount++;
			if ( runcount==63 )
			{
				if ( !( i=encputPCX( last,runcount,fptr ) ) )
					return(0);
				total += i;
				fptr += i;
				runcount = 0;
			}
		}
		else
		{                               // there ISNT a run.
			if ( runcount )             // so encode last run
			{
				if ( !(i=encputPCX( last,runcount,fptr ) ) )
					return(0);
				total += i;
				fptr += i;
			}
			last = current;
			runcount = 1;
		}
	}
	if ( runcount )
	{
		if ( !(i=encputPCX( last,runcount,fptr ) ) )
			return(0);
		total += i;
		fptr += i;
	}
	return( total );
}

/****************************************************************************
 *			encode byte run to PCX file.. given Byte and Runcount
 *                 returns 0 if error else number bytes encoded
 ***************************************************************************/
int		encputPCX( char byt, char count, char *fptr )
{
	if ( count ) 					// count should be >0 && <64
	{
		if ( count>63 )
			return(0);
		if ( (count==1) && (0xC0 != (0xC0&byt) ) )
		{
			*(fptr) = byt;          // only insert byte if we dont
			return(1);              // set run bits
		}
		else
		{
			*(fptr) = (0xC0|count);	// input count
			*(fptr+1) = byt;        // and input byte
			return(2);
		}
	}
	return(0);
}

void SaveDDBuffer(IDirectDrawSurface3 *pSurf)
	{
	static int nFile=0;
	static char header[18]={0,0,2,0,0,0,0,0,0,0,0,0,0x40,0x01,0x00,0x01,16,0};
	char filename[16];
	unsigned int	i,j,k;
	int				r,g,b;
	short* p,* p1,* pd;
	FILE* handle;

	DDSURFACEDESC ddsd;
	InitDXStruct(ddsd);
	if (DX_TRY(pSurf->GetSurfaceDesc(&ddsd)))
		return;
	if (ddsd.ddpfPixelFormat.dwRGBBitCount==8)
		{
		SaveScreen();
		return;
		}
	if (ddsd.ddpfPixelFormat.dwRGBBitCount!=16)
		{
		Log("I can only do 8 or 16 bit screen shots");
		return;
		}
	InitDXStruct(ddsd);
	if (!DX_FAILED(DD_LockSurface(pSurf,ddsd)))
		{
		p=(short*)ddsd.lpSurface;
		sprintf(filename,"tomb%04d.tga",nFile++);
		handle=fopen(filename,"wb");
		if (handle)
			{
			p1=(short*)&header[12];
			*p1++=short(ddsd.dwWidth);
			*p1=short(ddsd.dwHeight);
  			fwrite(header,18,1,handle);

			pd=(short*)malloc_ptr;
			p+=(ddsd.lPitch/2)*ddsd.dwHeight;
			for (i=0;i<ddsd.dwHeight;i++) 
				{
				for(j=0;j<ddsd.dwWidth;j++)
					{
					k=p[j];
					if (ddsd.ddpfPixelFormat.dwRBitMask==0xf800)	// 565 format
						{
						r=(k>>11)&0x1f;
						g=(k>>6)&0x1f;
						b=k&0x1f;
						*pd++=(r<<10)+(g<<5)+b;
						}
					else											// 555 format
						*pd++ = k;
					}
				p-=ddsd.lPitch/2;
				}
			fwrite(malloc_ptr,ddsd.dwWidth*ddsd.dwHeight*2,1,handle);
			fclose(handle);
			++filename[7];
			if(filename[7]>'9') 
				{
				filename[7]='0';
				++filename[6];
				}
			}
		DD_UnlockSurface(pSurf,ddsd);
		}
	}
