#include "pch.hpp"
//===========================================================================//
// File:	 WinMain.cpp													 //
// Project:  GameOS					                                         //
// Contents: WinMain routines												 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 11/04/97  SCP    Created													 //
// 12/05/97  JTR    Added lots of submanager, played with ordering			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Windows.hpp"
#include "Globals.hpp"
#include "DXRasterizer.hpp"
#include "Sound Renderer.hpp"
#include "ErrorHandler.hpp"
#include "MemoryManager.hpp"
#include "DirectX.hpp"
#include "FileIO.hpp"
#include "ControlManager.hpp"
#include "LocalizationManager.hpp"
#include "Time.hpp"
#include "Registry.hpp"
#include "Texture Manager.hpp"
#include "3DRasterizer.hpp"
#include "Debugger.hpp"
#include "Profiler.hpp"
#include "Font3D.hpp"
#include "VideoPlayback.hpp"
#include "Music.hpp"
#include "Perf.h"
#include "FileIO.hpp"
#include "String.hpp"

// jcem - start
#include "mreplay.h"
#include "mreplay.cpp"
void (__stdcall *g_pfnCTCL_Proc)() = NULL;
void (__stdcall *g_pfnCTCL_AfterBeginScene)() = NULL;
void (__stdcall *g_pfnCTCL_CoinDisplay)(bool bInsideBeginScene) = NULL;
int g_nCoinCount = 0;
int g_nCoinCountTemp = 0; // for temporary credits - used when invited from single game...
int g_nCoinPerGame = 2;
bool g_bUseCam = false;
bool g_bCOOP = false; // COin OPeration
bool g_bUseJPD = false; // Use Joystick as Pointing Device
bool g_bOldLOG = false;
// jcem - end

void(__stdcall *DoScriptUpdate)(int)=0;
void _stdcall InitNetworking();
void _stdcall UpdateNetwork();

extern void StartGosViewServerThreads();
extern void StopGosViewServerThreads();

float g_fGameEndScreenTimer = 0.0;
//
// DisplayIME flag
//
extern bool g_bComposingIME;
extern bool NoVideoPlayback;
extern int g_nTypeOfMFDs; // 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual)
//
// Paths to search for files GameOS requires
//
DWORD gNoFree=0;
DWORD StartupMemory=0;
extern DWORD GetVMSize();
DWORD gLoadAllSymbols=0;
DWORD gDirectX7=0;
DWORD gDisableForceFeedback=0;
DWORD gDisableJoystick=0;
DWORD gNumberCompatFlags=0;
DWORD gCompatFlags=0;
DWORD gCompatFlags1=0;
DWORD RenderersSkipped=0;
DWORD gNoFPU=0;
DWORD gEnableGosView=0;
DWORD gUseBlade=0;
char AssetsDirectory1[MAX_PATH]="";
char AssetsDirectory2[MAX_PATH]="";
char ImageHelpPath[MAX_PATH]="";
char ApplicationName[256];
DWORD gStartWithLog=0;
char InitialLogFile[256];
DWORD gEnableSpew=0;
DWORD gDisableSound=0;
DWORD LastModalLoop=0;
DWORD ModalLoop=0;
DWORD gDisableRDTSC=0;
DWORD gLocalize=0;
bool PerfCounters=0;
bool RealTerminate=0;
bool gShowAllocations=0;
bool gUse3D=0;
DWORD InGameLogicCounter=0;

char SpewOptions[256];
char PauseSpewOn[256];
bool gPauseSpew=0;
bool gNoDialogs=0;
bool gNoBlade=0;

extern HMODULE hModImagehlp;
extern __int64 StartTimeRenderers;
extern __int64 TimeToRenderNow;
extern ControlManagerDataStruct	g_CMData;
void GetProcessorInfo();
DWORD __stdcall RunGameOSLogic();
DWORD InternalRunGameOSLogic( void(*DoGameLogic)() );
void InitializeGOS( HINSTANCE hInst, LPSTR lpCmdLine );
char MachineInfoFile[260];
bool gDumpMachineInfo=0;

//
// Statistics
//
volatile __int64 TimeMM;
volatile __int64 TimeControls;
volatile __int64 TimeScripts=0;
volatile __int64 TimeScriptsUpdate=0;
volatile __int64 TimeGameLogic=0;
volatile __int64 TimeNetwork=0;
volatile __int64 TimeGameOS=0;
volatile __int64 TimeSoundStream=0;
volatile __int64 TimeGameOS1=0;
volatile __int64 TimeDebugger=0;
volatile __int64 TimeRenderers=0;
volatile __int64 TimeOutSideLoop=0;
volatile __int64 StartTimeOutSideLoop=0;
volatile __int64 TimeDisplay=0;
volatile __int64 TimeWindows=0;
volatile __int64 TimeEndScene=0;
volatile __int64 TimeFont3D=0;
volatile __int64 TimeD3D=0;
volatile __int64 TimeDXShow=0;
volatile __int64 TimeElementRenderers=0;
extern __int64 TimeMLR;
extern __int64 TimeClearViewPort;
extern __int64 TimeInClearViewPort;
DWORD	ModalScripts=0;			// ModalScripts


DWORD SavedSP=0;
DWORD SavedBP=0;
bool RunFullScreen=0;
//
// Global, which platform am I running on MFC, Game or DLL?
//
PlatformType Platform;

//
// We need to be able to see this so we can verify that the heap push/pop comes out even
// when calling the client's initgame, dogamelogic, updatedisplay, and terminategame
//
extern DWORD gCurrentHeapStackLevel;

// 
// By default, send the client's mallocs and news to ClientHeap;
//
//
extern HGOSHEAP ClientHeap;


//
// Main application entry point when run being run as a game
//
void RunFromOtherApp( HINSTANCE hInstance, HWND hWnd, char* CommandLine )
{
	hWindow=hWnd;
	InitializeGOS( hInstance, CommandLine );
}

//
// Main application entry point when being run as a game
//
int APIENTRY RunFromWinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow )
{
	__try
	{
		_asm mov SavedSP,esp								// Store stack pointer
		_asm mov SavedBP,ebp								// Store stack frame
		InitializeGOS( hInst, lpCmdLine );					// Setup Environment
		//
		// Main loop within GameOS
		//
		do
		{
			RunGameOSLogic();
		} while( !TerminateGame );
		ExitGameOS();										// Last minute cleanup routines
	}
	__except(ProcessException(GetExceptionInformation()))
	{
	}
	return 0;
}




//
// Command line parameters and a copy in lower case
//
static char Parameters[256];
static char LCParameters[256];
static char OptionParam[256];

//
// Check command line for a GameOS option, return NULL, 1 or a pointer to any parameters in quotes
//
char* CheckOption( char* Option )
{
	char* Param=strstr( LCParameters, Option );
	if( NULL==Param )
		return NULL;

	DWORD Offset=Param-LCParameters;
	DWORD Len=strlen(Option);
	OptionParam[0]=0;
	char* result=(char*)1;

	if( Param[Len]=='\"' || Param[Len]==' ' || Param[Len]==0 )
	{
		while( Param[Len]==' ' )
			Len++;

		if( Param[Len]=='\"' )
		{
			int LenOption=0;
			Len++;
			do
			{
				OptionParam[LenOption]=Param[Len];
				LenOption++;
				Len++;

			} while( Param[Len]!='\"' && Param[Len]!=0 );
			
			if( Param[Len]=='\"' )
			{
				OptionParam[LenOption]=0;
				Len++;
			}

			result=OptionParam;
		}

		while( Param[Len]==' ' )
			Len++;
		while( Parameters[Offset+Len-1]!=0 )
		{
			Parameters[Offset]=Parameters[Offset+Len];
			LCParameters[Offset]=LCParameters[Offset+Len];
			Offset++;
		}
		return result;
	}
	return (char*)1;
}







void InitializeGOS( HINSTANCE hInst, LPSTR lpCmdLine )
{
	hInstance=hInst;
	status=BeforeInit;
//
// See how much memory used by EXE and STATIC libs
//
	StartupMemory=GetVMSize();
//
// Copy command line, convert it into lower case
//
	if( 0==lpCmdLine )
		lpCmdLine="";
	CommandLine=Parameters;
	strcpy( Parameters, lpCmdLine );
	for( int t0=0; t0<strlen(lpCmdLine)+1; t0++ )
	{
		if( Parameters[t0]>='A' && Parameters[t0]<='Z' )
			LCParameters[t0]=(char)(Parameters[t0]+32);
		else
			LCParameters[t0]=Parameters[t0];
	}
//
// Check for GameOS parameters on the command line
//
	bool BadParameter=false;
	if( CheckOption( "/gosusesw" ) )
		gUseBlade=1;
	if( CheckOption( "/gosusehw" ) )
		gUse3D=1;
	if( CheckOption( "/gosnodialogs" ) )
		gNoDialogs=1;
	if( CheckOption( "/gosnojoystick" ) )
		gDisableJoystick=1;
	if( CheckOption( "/gosnoforcefeedback" ) )
		gDisableForceFeedback=1;
	if( CheckOption( "/gosnosound" ) )
		gDisableSound=1;
	if( CheckOption( "/gosnovideo" ) )
		NoVideoPlayback=1;
	Environment.MemoryManager = 0;
	if( CheckOption( "/gosnomemory" ) )
		Environment.MemoryManager=1;
	if( CheckOption( "/gosnoblade" ) )
		gNoBlade=1;
	if( CheckOption( "/goscheckmemory" ) )
		Environment.MemoryManager=2;
	if( CheckOption( "/gosdx" ) )
		gDirectX7=1;
	if( CheckOption( "/gosallsymbols" ) )
		gLoadAllSymbols=1;
	char* Option=CheckOption( "/gosinfo" );
	if( Option )
	{
		gNoDialogs=1;
		gDumpMachineInfo=1;
		if( Option==(char*)1 )
			strcpy( MachineInfoFile, "debuglog.txt" );
		else
			strcpy( MachineInfoFile, Option );
	}
	Option=CheckOption( "/gosconfig" );
	if( Option>(char*)1 )
	{
		gNumberCompatFlags=sscanf( Option, "%d %d", &gCompatFlags, &gCompatFlags1 );
	}
	else
	{
		if( Option )
			BadParameter=true;
	}
	#ifdef LAB_ONLY
	if( CheckOption( "/gosnordtsc" ) )
		gDisableRDTSC=1;
	if( CheckOption( "/gosshowmemory" ) )
		gShowAllocations=1;
	if( CheckOption( "/gosnofpu" ) )
		gNoFPU=1;
	if( CheckOption( "/gosnofree" ) )
		gNoFree=1;
	if( CheckOption( "/gosnofree1" ) )
		gNoFree=2;
	if( CheckOption( "/goslocalize" ) )
		gLocalize=1;
	if( CheckOption( "/gosview" ) )
		gEnableGosView=1;
	Option=CheckOption( "/gosstopon" );
	if( Option>(char*)1 )
	{
		gPauseSpew=1;
		strcpy( PauseSpewOn, Option );
	}
	else
	{
		if( Option )
			BadParameter=true;
	}
	Option=CheckOption( "/gosspew" );
	if( Option )
	{
		gEnableSpew=1;
		if( Option==(char*)1 )
			strcpy( SpewOptions, "*" );
		else
			strcpy( SpewOptions, Option );
	}
	Option=CheckOption( "/gosquickspew" );
	if( Option )
	{
		gEnableSpew=2;
		if( Option==(char*)1 )
			strcpy( SpewOptions, "*" );
		else
			strcpy( SpewOptions, Option );
	}
	Option=CheckOption( "/goslog" );
	if( Option>(char*)1 )
	{
		gStartWithLog=1;
		strcpy( InitialLogFile, Option );
	}
	else
	{
		if( Option )
			BadParameter=true;
	}
	if( BadParameter || NULL!=strstr( LCParameters, "/gos") )
	{
		MessageBox( NULL, "/gosNoDialogs		- Disable all GameOS dialogs\n" \
						  "/gosAllSymbols		- Load symbols for all DLLs with PDBs\n" \
						  "/gosLocalize 		- Pad all resource.dll loaded strings\n" \
						  "/gosDX 			- Allow DirectX 7.0a\n" \
						  "/gosUseSW 		- Disable 3D hardware\n" \
						  "/gosUseHW 		- Enable 3D hardware, ignore texture and caps\n" \
						  "/gosView 			- Enable remote profiling\n" \
						  "/gosSpew 		- Spew and flush everything to debuglog.txt\n" \
						  "/gosQuickSpew		- Spew everything to debuglog.txt\n" \
						  "/gosNoForceFeedback	- Disable forcefeedback joysticks\n"\
						  "/gosNoJoystick		- Disable joysticks\n"\
						  "/gosNoSound		- Disable sound\n"\
						  "/gosNoVideo		- Disable video playback\n"\
						  "/gosNoFPU		- Disable FPU exceptions\n"\
						  "/gosNoBlade		- Disable Blade rasterizer\n"\
						  "/gosShowMemory		- Spew all memory allocations and frees to debuglog.txt\n"\
						  "/gosNoMemory		- No memory management\n"\
						  "/gosNoFree		- Never free memory (all allocations unique)\n"\
						  "/gosNoFree1		- Never free memory (put esp contents in old block)\n"\
						  "/gosCheckMemory		- Check for memory read/writes beyond allocations\n"\
						  "/gosNoRDTSC		- Use real time clock in debugger\n"\
						  "/gosConfig \"xxx\"		- xxx is a decimal number used to override 3D compatibility flags\n"\
						  "/gosLog \"xxx\"		- Start game using log file \"xxx\"\n"\
						  "/gosStopOn \"xxx\"		- Cause all spews to \"xxx\" to PAUSE\n"\
						  "/gosInfo \"xxx\"		- Save exception details to \"xxx\" and quit\n"\
						  ,"GameOS case insensitive switches",MB_OK );
		ExitGameOS();
	}
	#else
	BadParameter=BadParameter;
	#endif
//
// Make sure exception handler is running
//
	if( 0==hModImagehlp )
		InitExceptionHandler( Parameters );
//
// Setup localization
//
	InitLocalization();					   
//
// Check the user is not trying to start two instances
//
	if( Platform==Platform_Game && !Environment.allowMultipleApps && AlreadyRunning() )
		ExitGameOS();
//
// Setup rest of GameOS
//
	RunFullScreen=Environment.fullScreen;
	GlobalFullScreen=Environment.fullScreen=0;			// Start in window mode always
	status=Uninitialized;								// Now all GOS functions can be used
	if( Platform!=Platform_DLL )
		InitLibraries();								// Load all libraries GOS depends upon
	InitDebugger();										// Setup debugger spew
	InitializeSpew();									// Activate error reporting
	TimeManagerInstall();								// Time controller
	SetupVirtualMemory();								// Setup virtual memory paging
	MM_Startup();										// Memory manager
	RegistryManagerInstall();							// Registry information
	Init_FileSystem();									// Init file system
//
// Now exceptions can be enabled
//
	gos_MathExceptions(1,0);

//
// Start the logging thread
//
	StartGosViewServerThreads();
//
// Init functions that depend on the memory manager being setup
//
	if( Platform!=Platform_DLL )
	{
		Init3DFont();										// Setup font routines
		if( !WindowsNT )
			PerfCounters=S_OK==InitPerformanceMonitorLibrary();	// Processor performance registers
		InitStatistics();									// Start statistics working
		InitRenderer();										// Setup 3D rendering variables
		InitializeWindows();								// Setup windows
		VideoManagerInstall();								// Video playback
//
// Init functions that depend on windows being setup
//
		InitTextureManager();								// Setup Texture manager
		DirectDrawInstall();								// Check DirectX version and setup DirectDraw  (Checks DX Version - Must be done before DSOUND and DPLAY!)
		InitDebuggerMenus();								// Setup device names in debugger menus
		CMInstall();										// Contol manager
		SoundRendererInstall(Environment.soundChannels);	// Sound system
		InitNetworking();									// Networking system
//
// Start log file recording or playback
//
		InitLogging();
		StartTimeManager();
//
// Setup other variables before app inited
//
		WatchDogFrame++;
		InGameLogicCounter=0;
//
// Dump a machine information file then exit?  (/gosinfo command line option)
//
		if( gDumpMachineInfo )
		{
			static FixedLengthString Buffer(16384);
			GetMachineDetails( Buffer );
			GetDirectXDetails( Buffer );
			GetInstalledAudioVideoCodecs( Buffer );
			char OutputBuffer[8192];
			char* Dest=OutputBuffer;
			char* Source=Buffer;
			while( *Source )
			{
				if( *Source==0xa )							// Replace 10 with 13,10
				{
					*Dest++=13;
					*Dest++=10;
				}
				else
					*Dest++=*Source;
				Source++;
			}
			*Dest=0;

			DWORD temp;
			char* Name=MachineInfoFile;
			if( Name[0]=='+' )
				Name++;
			HANDLE hFile = CreateFile( Name, GENERIC_WRITE, 0,NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
			if( hFile!=INVALID_HANDLE_VALUE )
			{
				if( MachineInfoFile[0]=='+' )
					SetFilePointer( hFile, 0, 0, FILE_END );
				WriteFile( hFile, OutputBuffer, Dest-OutputBuffer, &temp, NULL );
				CloseHandle( hFile );
			}
			ExitGameOS();
		}
//
// If user requested full screen, enter it
//
		status=GameInit;										// Now the shell is in the main loop
		if( RunFullScreen )
			EnterFullScreenMode();
//
// The game is the very last thing to be setup
//
		SPEW(( GROUP_GAMEOS, "InitializeGameEngine()" ));
		DWORD stackLevel = gCurrentHeapStackLevel;stackLevel;
		gos_PushCurrentHeap(ClientHeap);
		Environment.InitializeGameEngine();					// Setup the game application
		if (Platform != Platform_MFC)
		{
			gos_PopCurrentHeap();
#ifdef LAB_ONLY
			if (stackLevel > gCurrentHeapStackLevel)
				STOP(("The heap stack was popped more times than it was pushed"));
			else
			if (stackLevel < gCurrentHeapStackLevel)
				STOP(("The heap stack was pushed more times than it was popped"));
#endif
		}
		SPEW(( GROUP_GAMEOS, "*** Game Running ***" ));
	}
//
// Has the application ended the game during init?
//
	if( TerminateGame )
	{
//
// Clean up if need to exit game
//
		if( Platform==Platform_Game && TerminateGame && !ModalLoop && !ModalScripts )
		{
			DWORD stackLevel = gCurrentHeapStackLevel;stackLevel;
			gos_PushCurrentHeap(ClientHeap);stackLevel;
			RealTerminate=0;
			Environment.TerminateGameEngine();					// Tell Game to clean up and exit
			gos_PopCurrentHeap();
#ifdef LAB_ONLY
			if (stackLevel > gCurrentHeapStackLevel)
				STOP(("The heap stack was popped more times than it was pushed"));
			else
			if (stackLevel < gCurrentHeapStackLevel)
				STOP(("The heap stack was pushed more times than it was popped"));
#endif
		}
		if( TerminateGame )
			ExitGameOS();
	}
//
// Game is now running
//
	if( Platform!=Platform_DLL )
		status=Running;										// Now the shell is in the main loop
}






static DWORD fswid;
static DWORD fshei;
static DWORD fsdev;
static bool fsmode;
static DWORD stackLevel;
static DWORD FGcount;
//
// Reinit variables in GameOS for a game restart (ALL VARIABLES IN HERE MUST BE STATIC!!)
//
void RestartGameOS()
{
//
// Make sure there is a log present
//
	if( Platform!=Platform_Game )
		STOP(("RestartGameOS - Not valid unless running a game"));
	if( !SavedSP )
		return;
//
// Do not spew while restarting
//
	SpewSilence=0;
//
// If recording a log file, finish current frame
//
	if( 0==LogMode )
		EndLogging();
//
// Kill the Game
//
	stackLevel = gCurrentHeapStackLevel;
	gos_PushCurrentHeap(ClientHeap);
	Destroy3D();										// End any 3D scene
	RealTerminate=1;
	LogMode=1;
	InDebugger=1;										// make sure no more logging stuff happens
	Environment.TerminateGameEngine();					// Tell Game to clean up and exit
	InDebugger=0;
	gos_PopCurrentHeap();
#ifdef LAB_ONLY
	if (stackLevel > gCurrentHeapStackLevel)
		STOP(("The heap stack was popped more times than it was pushed"));
	else
	if (stackLevel < gCurrentHeapStackLevel)
		STOP(("The heap stack was pushed more times than it was popped"));
#endif
//
// Reset parts of GameOS
//
	Destroy3DFont();
	DestroyTextureManager();							// Clear all textures loaded
	SoundRendererUninstall();							// Close sound renderer
	VideoManagerUninstall();
	DestroyLocalization();
//
// Save debugger graphs active
//
	memset( ProfileFlags, 0, sizeof(ProfileFlags) );
	Stat* pStat=pStatistics;
	FGcount=0;
	while( pStat )
	{
		if( 0==(pStat->Flags&Stat_Format) )
		{
			if( pStat->Flags&Stat_Graph )
			{
				ProfileFlags[FGcount>>3]|=(1<<(FGcount&7));
			}
			FGcount++;
		}
		pStat=pStat->pNext;
	}
//
//
//
	DestroyStatistics();								// Stop all statistics
	CleanUpDebugger();									// Setup debugger spew
//
// Don't trigger watchdog timer
//
	WatchDogFrame++;
//
// Startup GameOS components again
//
	_asm mov esp,SavedSP;								// Restore stack pointer
	_asm mov ebp,SavedBP;								// Restore stack frame pointer
//
// Increment number of times looped counter
//
	LogLoops++;
//
// Start log file playback
//
	LogMode=1;
	InitLogging();
//
// Reset GameOS Environment
//
	fswid=Environment.screenWidth;
	fshei=Environment.screenHeight;
	fsdev=Environment.FullScreenDevice;
	fsmode=Environment.fullScreen;

	GetEnvironmentSettings(CommandLine);

	Environment.screenWidth=fswid;
	Environment.screenHeight=fshei;
	Environment.FullScreenDevice=fsdev;
	Environment.fullScreen=fsmode;

	GlobalWidth=Environment.screenWidth;
	GlobalHeight=Environment.screenHeight;
	GlobalZBuffer=Environment.disableZBuffer;
	GlobalFullScreen=Environment.fullScreen;

	if( gDisableSound )
		Environment.soundDisable=1;						// Disable sound if switch set
//
//
//
	InGameLogicCounter=0;
	TerminateGame=0;
	status=Uninitialized;								// Now all GOS functions can be used
	InDebugger=0;
	UpdatedExe=0;
	InitStatistics();									// Start statistics working
	ReInit3D();
	InitLocalization();
	InitTextureManager();								// Reinit texture manager
	gos_RecreateTextureHeaps();
	ReInitControlManager();
	Init3DFont();
	SoundRendererInstall(Environment.soundChannels);	// Sound system
	VideoManagerInstall();
	StartTimeManager();
//
// Don't trigger watchdog timer
//
	WatchDogFrame++;
//
// The game is the very last thing to be setup
//
	stackLevel = gCurrentHeapStackLevel;
	status=GameInit;									// Now the shell is in the main loop
	gos_PushCurrentHeap(ClientHeap);
	Environment.InitializeGameEngine();					// Setup the game application
	gos_PopCurrentHeap();
#ifdef LAB_ONLY
	if (stackLevel > gCurrentHeapStackLevel)
		STOP(("The heap stack was popped more times than it was pushed"));
	else
	if (stackLevel < gCurrentHeapStackLevel)
		STOP(("The heap stack was pushed more times than it was pushed"));
#endif
	status=Running;										// Now the shell is in the main loop
	GameOSFPU();
//
// Make sure any graphs enabled
//
	UpdateGraphs();
//
// Main loop within GameOS
//
	while( !TerminateGame )
	{
		RunGameOSLogic();
	} 
	ExitGameOS();
//
//
//
}
















extern volatile __int64 TimeUploadTextures;
extern volatile __int64 TimeConvertTextures;
extern volatile __int64 TimeMipmapTextures;
extern volatile __int64 TimeRenderTextures;
__int64 TUploadTexture;
__int64 TConvertTexture;
__int64 TMipmapTexture;
__int64 TRenderTextures;

//
// Macro used for statistic timing of main functions
//
#ifdef LAB_ONLY
#define ProfileTime(x,y)	x=GetCycles();y;x=GetCycles()-x;
#else
#define ProfileTime(x,y)	y;
#endif
//
// To catch MLR and element renderer timing these routines must be called
//
void ProfileRenderStart()
{
	TimeClearViewPort=0;
	TimeToRenderNow=0;
	TUploadTexture=TimeUploadTextures;
	TConvertTexture=TimeConvertTextures;
	TMipmapTexture=TimeMipmapTextures;
	TRenderTextures=TimeRenderTextures;
	StartTimeRenderers=GetCycles();
}
__int64 ProfileRenderEnd( __int64 RenderTime )
{
	if( 0==TimeToRenderNow )
		TimeToRenderNow=StartTimeRenderers;
	TimeD3D+=RenderTime-( (TimeToRenderNow-StartTimeRenderers)+
						  (TimeRenderTextures-TRenderTextures)+
						  (TimeUploadTextures-TUploadTexture)+
						  (TimeConvertTextures-TConvertTexture)+
						  (TimeMipmapTextures-TMipmapTexture) );
	if( TimeD3D<0 )
		TimeD3D=0;
	TimeInClearViewPort+=TimeClearViewPort;
	return (TimeToRenderNow-StartTimeRenderers)-TimeClearViewPort;
}


//sanghoon begin
#include "render.hpp"
HRESULT App_Render( LPDIRECT3DDEVICE7 pd3dDevice,LPDIRECTDRAWSURFACE7 pddsTexture);

//sanghoon end


//
// Update the display (call all the 'Update Renderers' routines
//
// If Everything is true the debugger is updated, endscene and flip occur. If not, just the game is rendered
//
void gos_UpdateDisplay( bool Everything )
{
	LOG_BLOCK("Gos::UpdateDisplay");
//
// Skip rendering for this one frame?
//
	if( SkipRendering )
	{
		SPEW(( GROUP_GAMEOS, "gos_UpdateDisplay() being skipped" ));
		SkipRendering=0;
		RenderersSkipped++;
		return;
	}
//
// Check if should not render (control alt delete pressed)
//
//	if( WindowsNT && 0!=(gos_GetMachineInformation(gos_Info_GetApplicationStatus) & 4) )
//		return;
//
//
//
	SPEW(( GROUP_GAMEOS, "gos_UpdateDisplay()" ));
//
// Recursion now allowed here at all.
//
	gosASSERT( !InUpdateRenderers );
//
// Allow some functions ONLY in update renderers
//
	InUpdateRenderers=1;
//
// Update the frame rate debugger stat
//
	__int64 thisframe=GetCycles()-StartOfRenderTime;			// Work out length of last frame
	if( thisframe==0 )
		thisframe=1;											// Avoid / 0

	frameRate=(float)frequency/(float)thisframe;				// Calculate frame rate based on clock frequency
	StartOfRenderTime=GetCycles();
//
// Don't trigger watchdog timer
//
	WatchDogFrame++;
//
// Check to see if window still valid
//
	if( !CheckWindow() )
	{
		InUpdateRenderers=0;
		return;
	}
//
// Update Video Manager
//
	if( (status==GameInit || status==Running) && (!gStopRendering && !gStopGameRendering) )
	{
		ProfileTime( TimeDXShow, VideoManagerUpdate() );
		TimeGameOS+= TimeDXShow;
		ProfileTime( TimeDXShow, MusicManagerUpdate() );
		TimeGameOS+= TimeDXShow;
	}
//
// If gStopRendering is set, or wireframe mode, or no update renderers, then clear viewport (unless skipping in log playback)
//
	if( (gStopGameRendering || gStopRendering) && 0==FrameAdvance )
	{
		ProfileTime( TimeClearViewPort, gos_SetupViewport( 0, 0, 1, 0x303050 , 0.0, 0.0, 1.0, 1.0 ) );
		TimeInClearViewPort+=TimeClearViewPort;
	}
	//sanghoon begin
	//Draw background image to backbuffer...

	if(hsh_initialized){
		// 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual)
		switch(g_nTypeOfMFDs) {
		case 1:
			if(sh_step==0){
				radar_device.BeginScene();
				mfd_device.BeginScene();
			}
			break;
		case 2:
			if(sh_step==0){
				//mfd_device.SetRenderTargetBackbuffer();
				//mfd_device.pD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET ,0,1.0f,0);
				/*{static D3DRECT s_rc2 = { 640, 0, 1280, 480 };
				mfd_device.SetRenderTargetBackbuffer();
				mfd_device.pD3DDevice->Clear(1,&s_rc2,D3DCLEAR_TARGET ,0,1.0f,0);
				//mfd_device.pD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET ,0,1.0f,0);
				}*/

				radar_device.BeginScene();
			}
			break;
		case 4:
			// Step 0: clear + grid for radar and LEFT MFD.
			// Step 1: clear + grid for RIGHT MFD (staggered so the right
			//   flip at new sh_step==1 shows channels 3-4 from the previous
			//   cycle, not a freshly-cleared buffer).
			if(sh_step==0){
				radar_device.BeginScene();
				mfd_device.BeginScene();  // left device only
			} else if(sh_step==1) {
				mfd_device.BeginSceneRight();  // right device clear + grid
			}
			break;
		}
	}else if(hsh_mrdev_initialized){
		mr_device.BeginScene();
	}

	//HSH_RenderAux1SmallMech();
	//sanghoon end
    if (g_pfnCTCL_AfterBeginScene) {
        (*g_pfnCTCL_AfterBeginScene)();
    }

//
// Do Scrips before Game's UpdateRenders
//
	if( DoScriptUpdate && !gStopRendering )
	{
		ProfileRenderStart();
		ProfileTime( TimeGameOS1, DoScriptUpdate(0x0000) );
		TimeScriptsUpdate+=TimeGameOS1;
		ProfileRenderEnd(TimeGameOS1);
	}
//
// Update Game's Renderers
//
	if( status==Running && (!gStopRendering && !gStopGameRendering) )
	{
		DWORD stackLevel = gCurrentHeapStackLevel;stackLevel;
		ProfileRenderStart();
		gos_PushCurrentHeap(ClientHeap);
		if( Environment.UpdateRenderers )
		{
			LOG_BLOCK("GosApp::UpdateRenderers");
			ProfileTime( TimeRenderers, Environment.UpdateRenderers() );
		}
		gos_PopCurrentHeap();
		ProfileRenderEnd(TimeRenderers);
#ifdef LAB_ONLY
		if (stackLevel > gCurrentHeapStackLevel)
			STOP(("The heap stack was popped more times than it was pushed"));
		else
		if (stackLevel < gCurrentHeapStackLevel)
			STOP(("The heap stack was pushed more times than it was popped"));
#endif
	}
//
// Do Scripts after Game's Renderers
//
	if( DoScriptUpdate && !gStopRendering )
	{
		LOG_BLOCK("Gos::DoScriptUpdate(0x1000)");
		ProfileRenderStart();
		ProfileTime( TimeGameOS1, DoScriptUpdate(0x1000) );
		TimeScriptsUpdate+=TimeGameOS1;
		ProfileRenderEnd(TimeGameOS1);
	}
//
// Display IME
//
	{
		LOG_BLOCK("Gos::RenderIME");
		RenderIME();
	}

	if( DoScriptUpdate && !gStopRendering )
	{
		LOG_BLOCK("Gos::DoScriptUpdate(0x4000)");
		ProfileRenderStart();
		ProfileTime( TimeGameOS1, DoScriptUpdate(0x4000) );
		TimeScriptsUpdate+=TimeGameOS1;
		ProfileRenderEnd(TimeGameOS1);
	}

	if (g_pfnCTCL_CoinDisplay) {
		(*g_pfnCTCL_CoinDisplay)(InsideBeginScene ? true: false);
	}

//
// Do the sound renderer functions... even in log playback
// because endframe commits all the changes made to
// 3D sound buffers. If we didn't commit the deferred commands,
// playback wouldn't be correct.
//
	if ( status==Running )
	{
		LOG_BLOCK("GosAudio::SoundRendererEndFrame");
		ProfileTime( TimeGameOS1, SoundRendererEndFrame() );
		TimeGameOS+=TimeGameOS1;
	}
//
// If only rendering the game scene, return
//
	if( !Everything )
	{
		InUpdateRenderers=0;
		return;
	}
//
// Debugger - Render debug window or framegraphs
//
	{
		LOG_BLOCK("Gos::UpdateDebugger()");
		ProfileTime( TimeDebugger, UpdateDebugger() );
	}
	//sanghoon
#if 1
	extern LPDIRECT3DDEVICE7	d3dDevice7;
	static bool preload_GameEndScreem=true;;
    if (0.0 < g_fGameEndScreenTimer) {
        if (gos_GetElapsedTime() < g_fGameEndScreenTimer) {
	        extern IDirectDrawSurface7*	SH_GameEndTexture;
            extern void __cdecl DrawGameEndScreen(LPDIRECT3DDEVICE7	pd3dDevice,LPDIRECTDRAWSURFACE7	pTexture,bool freload);
		    DrawGameEndScreen(d3dDevice7,SH_GameEndTexture,preload_GameEndScreem);
		    preload_GameEndScreem=false;
        } else {
            g_fGameEndScreenTimer = 0.0;
            preload_GameEndScreem = true;
        }
        // draw
    } else {
        preload_GameEndScreem = true;
    }
#endif
//
// Only EndScene - display is flipped right before next game logic
//
	{
		LOG_BLOCK("Gos::End3DScene()");
		ProfileTime( TimeEndScene, End3DScene() );
	}
//
// Allow non renderer functions again
//
	InUpdateRenderers=0;
	RenderersSkipped=0;
//
// Flip or Blit the current image onto the display
//
	//sanghoon begin
	//Perform the Flip.
	if(hsh_initialized){
		// 0 - no MFDs, 1 - orginal(5+1=dual & single), 2 - B&W(3 + 1=1 dual), 3 - color(3 + 1=2 dual)
		switch(g_nTypeOfMFDs) {
		case 1:
			sh_step++;
			sh_step%=7;
			if(sh_step==0) {
				radar_device.EndScene();
				radar_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC );
				mfd_device.EndScene();
				mfd_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC );
			}
			break;
		case 2:
			sh_step++;
			sh_step%=5;
			if (sh_step==2) {
				radar_device.EndScene();
				//radar_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC );
				mfd_device.BeginScene();
			} else if(sh_step==0) {
				mfd_device.EndScene();
				mfd_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC );
			}
			break;
		case 3:
			break;
		case 4:
			// 7-step cadence.  Radar + left MFD flip on step 0 (same as mode 1).
			// Right MFD flip is staggered to step 1 so that three back-to-back
			// DGVoodoo2 Present() calls don't all land in the same frame and
			// cause rhythmic stutter on the main display.
			// Right device finishes rendering at step 6 (channel 4), so
			// flipping at step 1 is still correct timing.
			sh_step++;
			sh_step%=7;
			if(sh_step==0) {
				radar_device.EndScene();
				radar_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC);
				mfd_device.EndScene();
				mfd_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC);
			} else if(sh_step==1) {
				if (mfd_device_right.pDDSFront)
					mfd_device_right.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC);
			}
			break;
		}
	}else if(hsh_mrdev_initialized){
		mr_device.EndScene();
		mr_device.pDDSFront->Flip(0,DDFLIP_DONOTWAIT|DDFLIP_NOVSYNC);
	}

	//sanghoon end
	{
		LOG_BLOCK("Gos::DisplayBackBuffer()");
		ProfileTime( TimeDisplay, DisplayBackBuffer() );
	}

}

//
// Main loop within GameOS
//
DWORD InternalRunGameOSLogic( void(*DoGameLogic)() )
{
	LOG_START_CHECK
	LOG_BLOCK("Gos::InternalRunGameOSLogic");

	// jcem - start
	if (g_pfnCTCL_Proc) {
		(*g_pfnCTCL_Proc)();
	}
	// jcem - end

//
// How long spent outside loop?
//
	TimeOutSideLoop=GetCycles()-StartTimeOutSideLoop;
//
// Start / stop showing a single frame of data?
//
	if( gShowMemoryUseage )
		gShowMemoryUseage--;
	if( gShowWSUseage )
	{
		if( gShowWSUseage==2 )
		{
			pMemBlockInfo=(PSAPI_WS_WATCH_INFORMATION*)gos_Malloc(16*1024);
		    HWSProcess = GetCurrentProcess();
			if( !_EmptyWorkingSet(HWSProcess) )
			{
				gShowWSUseage=0;
			}
			else
			{
				if( !_InitializeProcessForWsWatch(HWSProcess) )
					gShowWSUseage=0;
				else
				{
					PSAPI_WS_WATCH_INFORMATION* ppMemBlock=pMemBlockInfo;
					_GetWsChanges(HWSProcess, ppMemBlock, 16*1024);
				}
			}
		}
		if( gShowWSUseage==1 )
		{
			AnalyzeWS( "After Update Renderers" );
			gos_Free(pMemBlockInfo);
			pMemBlockInfo=0;
		}
		if( gShowWSUseage )
			gShowWSUseage--;
	}
	if( gShowFTOLUseage )
		gShowFTOLUseage--;
	if( gShowALLUseage )
		gShowALLUseage--;
//
// Check if threads have ended
//
	CheckThreads();
//
// Reset all statistics at the start of a frame
//
	ResetStatistics();
//
// Increment internal frame number
//	
	CurrentFrameNumber++;							// Which frame
//
// Recursion now allowed here at all, unless in a MODAL game loop.
//
	if( !ModalLoop && !ModalScripts && InGameLogicCounter )
		STOP(( "GameOS has become reentrant" ));
	InGameLogicCounter++;
//
// Don't trigger watchdog timer
//
	WatchDogFrame++;
//
// Read the 'now' time for this frame
//
	TimeManagerUpdate();
//
// Save the old keystates before reading windows messages
//
	SaveOldKeyState();

//
// Read windows messages - do not do this on MFC platform
//
	if( Platform != Platform_MFC )
	{
		gos_PushCurrentHeap(ClientHeap);
		ProfileTime( TimeWindows, Update() );
		gos_PopCurrentHeap();
	}
//
// Has the window been closed?
//
	if( WindowClosed )
	{
		WindowClosed=0;
		TerminateGame=2;
	}
//
// Update controls
//
	{
		LOG_BLOCK("Gos::CMUpdate ( AnalyzeControls )");
		ProfileTime( TimeControls, CMUpdate() );
		TimeGameOS+=TimeControls;
	}
//
// Send/Recieve network packets
//
	ProfileTime( TimeNetwork, UpdateNetwork() );
//
// Check log file for playback or record
//
	UpDateLogFile();
//
// Make sure math exceptions are enabled
//
	gos_MathExceptions(1,0);
//
// Grab working set before GameLogic
//
	if( gShowWSUseage==1 )
	{
		AnalyzeWS( "Up to GameLogic" );
	}
//
// Do game logic (if debugger allows)
//
	if( !ModalScripts && !TerminateGame && DoGameLogic && 0==gFreezeLogic && !(DebuggerActive && (!gGameLogicActive || gStopSystem)) )
	{
		DWORD stackLevel = gCurrentHeapStackLevel;stackLevel;
		gos_PushCurrentHeap(ClientHeap);
		{
			LOG_BLOCK( "GosApp::DoGameLogic" );
			ProfileTime( TimeGameLogic, DoGameLogic() );
		}
		gos_PopCurrentHeap();
#ifdef LAB_ONLY
		if (stackLevel > gCurrentHeapStackLevel)
			STOP(("The heap stack was popped more times than it was pushed"));
		else
		if (stackLevel < gCurrentHeapStackLevel)
			STOP(("The heap stack was pushed more times than it was popped"));
#endif
	}
//
// Grab working set after GameLogic
//
	if( gShowWSUseage==1 )
	{
		AnalyzeWS( "GameLogic" );
	}
//
// Do Scripts Execution (events - no renderers)
//
	if( !TerminateGame && DoScriptUpdate && 0==gFreezeLogic && !(DebuggerActive && (!gGameLogicActive || gStopSystem)) )
	{
		LOG_BLOCK("Gos::DoScriptUpdate");
		ProfileTime( TimeScripts, DoScriptUpdate(0xFFFF) );
	}
//
// At this point, make sure log file playback is still in sync
//
	if( !TerminateGame && 0==gFreezeLogic && !(DebuggerActive && (!gGameLogicActive || gStopSystem)) )
	{
		LOG_BLOCK("Gos::CheckLogInSync");
		CheckLogInSync();
	}
//
//	Set the Localization flags to purge
//
	LocalizationSetPurgeFlags();
//
// Grab working set after scripts logic
//
	if( gShowWSUseage==1 )
	{
		AnalyzeWS( "Scripts Execution" );
	}
//
// Update the display (call all the 'Update Renderers' routines
//
	if( !TerminateGame )
	{
		LOG_BLOCK("Gos::UpdateDisplay(1)");
		gos_UpdateDisplay(1);
	}
//
// Grab working set after update display
//
	if( gShowWSUseage==1 )
	{
		AnalyzeWS( "UpdateRenderers" );
	}
//
//	Do Localization purge check. Dicard any that weren't used this frame
//
	LocalizationPurge();
//
// Make sure math exceptions are disabled for windows messages and end scene
//
	gos_MathExceptions(0,0);
//
// Clean up if need to exit game
//
	if( TerminateGame && Platform==Platform_Game && !ModalLoop && !ModalScripts )
	{
		DWORD stackLevel = gCurrentHeapStackLevel;stackLevel;
		gos_PushCurrentHeap(ClientHeap);
		if( DebuggerActive && TerminateGame==2 )
		{
			RealTerminate=1;
		}
		else
		{
			RealTerminate=(1==LogMode ? 1 : 0);
		}
		Environment.TerminateGameEngine();					// Tell Game to clean up and exit
		gos_PopCurrentHeap();
#ifdef LAB_ONLY
		if (stackLevel > gCurrentHeapStackLevel)
			STOP(("The heap stack was popped more times than it was pushed"));
		else
		if (stackLevel < gCurrentHeapStackLevel)
			STOP(("The heap stack was pushed more times than it was popped"));
#endif
	}
//
// Leave GameLogic
//
	InGameLogicCounter--;
//
// SPEW results of float to long collection?
//
	#ifdef _DEBUG
	if( 1==gShowFTOLUseage )
	{
		ShowFTOL();
	}
	#endif
//
// Work out how much time spent OUTSIDE this loop (should only have an effect in MFC apps)
//
	StartTimeOutSideLoop=GetCycles();
//
// Return a flag if game terminated
//
	return TerminateGame;
}




//
// MFC Applications call this directly
//
DWORD __stdcall RunGameOSLogic()
{
	return InternalRunGameOSLogic( Environment.DoGameLogic );
}



//
// Can be called by either a script or game application to end the app.
//
void __stdcall gos_TerminateApplication()
{
	TerminateGame = 1;
}
//
// Can be called by either a script or game application to end the app.
//
void __stdcall gos_AbortTermination()
{
	TerminateGame = 0;
}

//
// Returns true if the application must really terminate
//
bool __stdcall gos_UserExit()
{
	return !RealTerminate;
}



//
// Allow applications to run modal main loops.
//
bool __stdcall gos_RunMainLoop( void(__stdcall *DoGameLogic)() )
{
	LOG_BLOCK("Gos::RunMainLoop")
	gosASSERT( !InUpdateRenderers );
	ModalLoop++;
	InternalRunGameOSLogic( DoGameLogic );
	ModalLoop--;
	return 0!=TerminateGame;
}

