//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//===========================================================================//
#define DISABLE_PROTECTED_THINGS

#if defined( USE_SDL )
#include "appframework/ilaunchermgr.h"
#endif

#if defined( _WIN32 ) && !defined( _X360 )
#include "winlite.h"
#include <Psapi.h>
#endif

#if defined( OSX )
#include <sys/sysctl.h>
#endif

#if defined( POSIX )
#include <setjmp.h>
#include <signal.h>
#endif

#include <stdarg.h>
#include "quakedef.h"
#include "idedicatedexports.h"
#include "engine_launcher_api.h"
#include "ivideomode.h"
#include "common.h"
#include "iregistry.h"
#include "keys.h"
#include "cdll_engine_int.h"
#include "traceinit.h"
#include "iengine.h"
#include "igame.h"
#include "tier0/etwprof.h"
#include "tier0/vcrmode.h"
#include "tier0/icommandline.h"
#include "tier0/minidump.h"
#include "engine_hlds_api.h"
#include "filesystem_engine.h"
#include "cl_main.h"
#include "client.h"
#include "tier3/tier3.h"
#include "MapReslistGenerator.h"
#include "toolframework/itoolframework.h"
#include "sourcevr/isourcevirtualreality.h"
#include "DevShotGenerator.h"
#include "gl_shader.h"
#include "l_studio.h"
#include "IHammer.h"
#include "sys_dll.h"
#include "materialsystem/materialsystem_config.h"
#include "server.h"
#include "video/ivideoservices.h"
#include "datacache/idatacache.h"
#include "vphysics_interface.h"
#include "inputsystem/iinputsystem.h"
#include "appframework/IAppSystemGroup.h"
#include "tier0/systeminformation.h"
#include "host_cmd.h"
#ifdef _WIN32
#include "VGuiMatSurface/IMatSystemSurface.h"
#endif

#ifdef GPROFILER
#include "gperftools/profiler.h"
#endif

// This is here just for legacy support of older .dlls!!!
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "eiface.h"
#include "tier1/fmtstr.h"
#include "steam/steam_api.h"

#ifndef SWDS
#include "sys_mainwind.h"
#include "vgui/ISystem.h"
#include "vgui_controls/Controls.h"
#include "IGameUIFuncs.h"
#include "cl_steamauth.h"
#endif // SWDS

#if defined(_WIN32)
#include <eh.h>
#endif

#if POSIX
#include <dlfcn.h>
#endif

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#else
#include "xbox/xboxstubs.h"
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
IDedicatedExports *dedicated = NULL;
extern CreateInterfaceFn g_AppSystemFactory;
IHammer *g_pHammer = NULL;
IPhysics *g_pPhysics = NULL;
ISourceVirtualReality *g_pSourceVR = NULL;
#if defined( USE_SDL )
ILauncherMgr *g_pLauncherMgr = NULL;
#endif

#ifndef SWDS
extern CreateInterfaceFn g_ClientFactory;
#endif

static SteamInfVersionInfo_t g_SteamInfIDVersionInfo;
const SteamInfVersionInfo_t& GetSteamInfIDVersionInfo()
{
	Assert( g_SteamInfIDVersionInfo.AppID != k_uAppIdInvalid );
	return g_SteamInfIDVersionInfo;
}

int build_number( void )
{
	return GetSteamInfIDVersionInfo().ServerVersion;
}

//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
void Host_GetHostInfo(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen );
const char *Key_BindingForKey( int keynum );
void COM_ShutdownFileSystem( void );
void COM_InitFilesystem( const char *pFullModPath );
void Host_ReadPreStartupConfiguration();

//-----------------------------------------------------------------------------
// ConVars and console commands
//-----------------------------------------------------------------------------
#ifndef SWDS
//-----------------------------------------------------------------------------
// Purpose: exports an interface that can be used by the launcher to run the engine
//			this is the exported function when compiled as a blob
//-----------------------------------------------------------------------------
void EXPORT F( IEngineAPI **api )
{
	CreateInterfaceFn factory = Sys_GetFactoryThis();	// This silly construction is necessary to prevent the LTCG compiler from crashing.
	*api = ( IEngineAPI * )(factory(VENGINE_LAUNCHER_API_VERSION, NULL));
}
#endif // SWDS

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ClearIOStates( void )
{
#ifndef SWDS
	if ( g_ClientDLL ) 
	{
		g_ClientDLL->IN_ClearStates();
	}
#endif
}

//-----------------------------------------------------------------------------
// The SDK launches the game with the full path to gameinfo.txt, so we need
// to strip off the path.
//-----------------------------------------------------------------------------
const char *GetModDirFromPath( const char *pszPath )
{
	char *pszSlash = Q_strrchr( pszPath, '\\' );
	if ( pszSlash )
	{
		return pszSlash + 1;
	}
	else if ( ( pszSlash  = Q_strrchr( pszPath, '/' ) ) != NULL )
	{
		return pszSlash + 1;
	}

	// Must just be a mod directory already.
	return pszPath;
}

//-----------------------------------------------------------------------------
// Purpose: Main entry
//-----------------------------------------------------------------------------
#ifndef SWDS
#include "gl_matsysiface.h"
#endif

//-----------------------------------------------------------------------------
// Inner loop: initialize, shutdown main systems, load steam to 
//-----------------------------------------------------------------------------
class CModAppSystemGroup : public CAppSystemGroup
{
	typedef CAppSystemGroup BaseClass;
public:
	// constructor
	CModAppSystemGroup( bool bServerOnly, CAppSystemGroup *pParentAppSystem = NULL )
		: BaseClass( pParentAppSystem ),
		m_bServerOnly( bServerOnly )
	{
	}

	CreateInterfaceFn GetFactory()
	{
		return CAppSystemGroup::GetFactory();
	}

	// Methods of IApplication
	virtual bool Create();
	virtual bool PreInit();
	virtual int Main();
	virtual void PostShutdown();
	virtual void Destroy();

private:

	bool IsServerOnly() const
	{
		return m_bServerOnly;
	}
	bool ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName );

	bool AddLegacySystems();
	bool	m_bServerOnly;
};

#if defined( STAGING_ONLY )
CON_COMMAND( bigalloc, "huge alloc crash" )
{
	Msg( "pre-crash %d\n", MemAlloc_MemoryAllocFailed() );
	// Alloc a bit less than UINT_MAX so there is room for heap headers in the malloc functions.
	void *buf = malloc( UINT_MAX - 0x4000 );
	Msg( "post-alloc %d. buf: %p\n", MemAlloc_MemoryAllocFailed(), buf );
	*(int *)buf = 0;
}
#endif

extern void S_ClearBuffer();
extern char g_minidumpinfo[ 4096 ];
extern PAGED_POOL_INFO_t g_pagedpoolinfo;
extern bool g_bUpdateMinidumpComment;
void GetSpew( char *buf, size_t buflen );

extern int gHostSpawnCount;
extern int g_nMapLoadCount;
extern int g_HostServerAbortCount;
extern int g_HostErrorCount;
extern int g_HostEndDemo;

// Turn this to 1 to allow for expanded spew in minidump comments.
static ConVar sys_minidumpexpandedspew( "sys_minidumpexpandedspew", "1" );

#ifdef IS_WINDOWS_PC

extern "C" void __cdecl FailSafe( unsigned int uStructuredExceptionCode, struct _EXCEPTION_POINTERS * pExceptionInfo	)
{
	// Nothing, this just catches a crash when creating the comment block
}

#endif

#if defined( POSIX )

static sigjmp_buf g_mark;
static void posix_signal_handler( int i )
{
	siglongjmp( g_mark, -1 );
}

#define DO_TRY		if ( sigsetjmp( g_mark, 1 ) == 0 )
#define DO_CATCH	else

#if defined( OSX )
#define __sighandler_t sig_t
#endif

#else

#define DO_TRY		try
#define DO_CATCH	catch ( ... )

#endif // POSIX

//-----------------------------------------------------------------------------
// Purpose: Check whether any mods are loaded.
//  Currently looks for metamod and sourcemod.
//-----------------------------------------------------------------------------
static bool IsSourceModLoaded()
{
#if defined( _WIN32 )
	static const char *s_pFileNames[] = { "metamod.2.tf2.dll", "sourcemod.2.tf2.dll", "sdkhooks.ext.2.ep2v.dll", "sdkhooks.ext.2.tf2.dll" };

	for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ )
	{
		// GetModuleHandle function returns a handle to a mapped module
		//  without incrementing its reference count.
		if ( GetModuleHandleA( s_pFileNames[ i ] ) )
			return true;
	}
#else
	FILE *fh = fopen( "/proc/self/maps", "r" );

	if ( fh )
	{
		char buf[ 1024 ];
		static const char *s_pFileNames[] = { "metamod.2.tf2.so", "sourcemod.2.tf2.so", "sdkhooks.ext.2.ep2v.so", "sdkhooks.ext.2.tf2.so" };

		while ( fgets( buf, sizeof( buf ), fh ) )
		{
			for ( size_t i = 0; i < Q_ARRAYSIZE( s_pFileNames ); i++ )
			{
				if ( strstr( buf, s_pFileNames[ i ] ) )
				{
					fclose( fh );
					return true;
				}
			}
		}

		fclose( fh );
	}
#endif

	return false;
}

template< int _SIZE >
class CErrorText
{
public:
	CErrorText() : m_bIsDedicatedServer( false ) {}
	~CErrorText() {}

	void Steam_SetMiniDumpComment()
	{
#if !defined( NO_STEAM )
		SteamAPI_SetMiniDumpComment( m_errorText );
#endif
	}

	void CommentCat( const char * str )
	{
		V_strcat_safe( m_errorText, str );
	}

	void CommentPrintf( const char *fmt, ... )
	{
		va_list args;
		va_start( args, fmt );
		
		size_t len = strlen( m_errorText );
		vsnprintf( m_errorText + len, sizeof( m_errorText ) - len - 1, fmt, args );
		m_errorText[ sizeof( m_errorText ) - 1 ] = 0;

		va_end( args );
	}

	void BuildComment( char const *pchSysErrorText, bool bRealCrash )
	{
		// Try and detect whether this
		bool bSourceModLoaded = false;
		if ( m_bIsDedicatedServer )
		{
			bSourceModLoaded = IsSourceModLoaded();
			if ( bSourceModLoaded )
			{
				AppId_t AppId = GetSteamInfIDVersionInfo().ServerAppID;
				// Bump up the number and report the crash. This should be something
				//  like 232251 (instead of 232250). 232251 is for the TF2 Windows client,
				//  but we actually report those crashes under ID 440, so this should be ok.
				SteamAPI_SetBreakpadAppID( AppId + 1 );
			}
		}

#ifdef IS_WINDOWS_PC
		// This warning only applies if you want to catch structured exceptions (crashes)
		// using C++ exceptions. We do not want to do that so we can build with C++ exceptions
		// completely disabled, and just suppress this warning.
		// warning C4535: calling _set_se_translator() requires /EHa
		#pragma warning( suppress : 4535 )
		_se_translator_function curfilter = _set_se_translator( &FailSafe );
#elif defined( POSIX )
		// Only need to worry about this function crashing when we're dealing with a real crash.
		__sighandler_t curfilter = bRealCrash ? signal( SIGSEGV, posix_signal_handler ) : 0;
#endif

		DO_TRY
		{
			Q_memset( m_errorText, 0x00, sizeof( m_errorText ) );

			if ( pchSysErrorText )
			{
				CommentCat( "Sys_Error( " );
				CommentCat( pchSysErrorText );

				// Trim trailing \n.
				int len = V_strlen( m_errorText );
				if ( len > 0 && m_errorText[ len - 1 ] == '\n' )
					m_errorText[ len - 1 ] = 0;

				CommentCat( " )\n" );
			}
			else
			{
				CommentCat( "Crash\n" );
			}
			CommentPrintf( "Uptime( %f )\n", Plat_FloatTime() );
			CommentPrintf( "SourceMod:%d,DS:%d,Crash:%d\n\n", bSourceModLoaded, m_bIsDedicatedServer, bRealCrash );

			// Add g_minidumpinfo from CL_SetSteamCrashComment().
			CommentCat( g_minidumpinfo );

			// Latch in case extended stuff below crashes
			Steam_SetMiniDumpComment();

			// Add Memory Status
			BuildCommentMemStatus();

			// Spew out paged pool stuff, etc.
			PAGED_POOL_INFO_t ppi_info;
			if ( Plat_GetPagedPoolInfo( &ppi_info ) != SYSCALL_UNSUPPORTED )
			{
				CommentPrintf( "\nPaged Pool\nprev PP PAGES: used: %lu, free %lu\nfinal PP PAGES: used: %lu, free %lu\n",
				            g_pagedpoolinfo.numPagesUsed, g_pagedpoolinfo.numPagesFree, 
				            ppi_info.numPagesUsed, ppi_info.numPagesFree );
			}

			CommentPrintf( "memallocfail? = %u\nActive: %s\nSpawnCount %d MapLoad Count %d\nError count %d, end demo %d, abort count %d\n",
			            MemAlloc_MemoryAllocFailed(),
			            ( game && game->IsActiveApp() ) ? "active" : "inactive", 
			            gHostSpawnCount, 
			            g_nMapLoadCount,
			            g_HostErrorCount,
			            g_HostEndDemo,
			            g_HostServerAbortCount );

			// Latch in case extended stuff below crashes
			Steam_SetMiniDumpComment();

			// Add user comment strings. 4096 is just a large sanity number we should
			//  never ever reach (currently our minidump supports 32 of these.)
			for( int i = 0; i < 4096; i++ )
			{
				const char *pUserStreamInfo = MinidumpUserStreamInfoGet( i );
				if( !pUserStreamInfo )
					break;

				if ( pUserStreamInfo[ 0 ] )
					CommentPrintf( "%s", pUserStreamInfo );
			}

			bool bExtendedSpew = sys_minidumpexpandedspew.GetBool();
			if ( bExtendedSpew )
			{
				BuildCommentExtended();
				Steam_SetMiniDumpComment();

#if defined( LINUX )
				if ( bRealCrash )
				{
					// bRealCrash is set when we're actually making a comment for a dump or error.
					AddFileToComment( "/proc/meminfo" );
					AddFileToComment( "/proc/self/status" );
					Steam_SetMiniDumpComment();

					// Useful, but really big, so disable for now.
					//$ AddFileToComment( "/proc/self/maps" );
				}
#endif
			}
		}
		DO_CATCH
		{
			// Oh oh
		}
		
#ifdef IS_WINDOWS_PC
		_set_se_translator( curfilter );
#elif defined( POSIX )
		if ( bRealCrash )
			signal( SIGSEGV, curfilter );
#endif
	}

	void BuildCommentMemStatus()
	{
#ifdef _WIN32
		const double MbDiv = 1024.0 * 1024.0;

		MEMORYSTATUSEX	memStat;
		ZeroMemory( &memStat, sizeof( MEMORYSTATUSEX ) );
		memStat.dwLength = sizeof( MEMORYSTATUSEX );

		if ( GlobalMemoryStatusEx( &memStat ) )
		{
			CommentPrintf( "\nMemory\nmemusage( %d %% )\ntotalPhysical Mb(%.2f)\nfreePhysical Mb(%.2f)\ntotalPaging Mb(%.2f)\nfreePaging Mb(%.2f)\ntotalVirtualMem Mb(%.2f)\nfreeVirtualMem Mb(%.2f)\nextendedVirtualFree Mb(%.2f)\n",
				memStat.dwMemoryLoad,
				(double)memStat.ullTotalPhys / MbDiv,
				(double)memStat.ullAvailPhys / MbDiv,
				(double)memStat.ullTotalPageFile / MbDiv,
				(double)memStat.ullAvailPageFile / MbDiv,
				(double)memStat.ullTotalVirtual / MbDiv,
				(double)memStat.ullAvailVirtual / MbDiv,
				(double)memStat.ullAvailExtendedVirtual / MbDiv);
		}

		HINSTANCE hInst = LoadLibrary( "Psapi.dll" );
		if ( hInst )
		{
			typedef BOOL (WINAPI *GetProcessMemoryInfoFn)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
			GetProcessMemoryInfoFn fn = (GetProcessMemoryInfoFn)GetProcAddress( hInst, "GetProcessMemoryInfo" );
			if ( fn )
			{
				PROCESS_MEMORY_COUNTERS counters;

				ZeroMemory( &counters, sizeof( PROCESS_MEMORY_COUNTERS ) );
				counters.cb = sizeof( PROCESS_MEMORY_COUNTERS );

				if ( fn( GetCurrentProcess(), &counters, sizeof( PROCESS_MEMORY_COUNTERS ) ) )
				{
					CommentPrintf( "\nProcess Memory\nWorkingSetSize Mb(%.2f)\nQuotaPagedPoolUsage Mb(%.2f)\nQuotaNonPagedPoolUsage: Mb(%.2f)\nPagefileUsage: Mb(%.2f)\n",
						(double)counters.WorkingSetSize / MbDiv,
						(double)counters.QuotaPagedPoolUsage / MbDiv,
						(double)counters.QuotaNonPagedPoolUsage / MbDiv,
						(double)counters.PagefileUsage / MbDiv );
				}
			}

			FreeLibrary( hInst );
		}

#elif defined( OSX )

		static const struct
		{
			int ctl;
			const char *name;
		} s_ctl_names[] =
		{
		#define _XTAG( _x ) { _x, #_x }
			_XTAG( HW_PHYSMEM ),
			_XTAG( HW_USERMEM ),
			_XTAG( HW_MEMSIZE ),
			_XTAG( HW_AVAILCPU ),
		#undef _XTAG
		};

		for ( size_t i = 0; i < Q_ARRAYSIZE( s_ctl_names ); i++ )
		{
			uint64_t val = 0;
			size_t len = sizeof( val );
			int mib[] = { CTL_HW, s_ctl_names[ i ].ctl };

			if ( sysctl( mib, Q_ARRAYSIZE( mib ), &val, &len, NULL, 0 ) == 0 )
			{
				CommentPrintf( " %s: %" PRIu64 "\n", s_ctl_names[ i ].name, val );
			}
		}

#endif
	}

	void BuildCommentExtended()
	{
		try
		{
			CommentCat( "\nConVars (non-default)\n\n" );
			CommentPrintf( "%s %s %s\n", "var", "value", "default" );

			for ( const ConCommandBase *var = g_pCVar->GetCommands() ; var ; var = var->GetNext())
			{
				if ( var->IsCommand() )
					continue;

				ConVar *pCvar = ( ConVar * )var;
				if ( pCvar->IsFlagSet( FCVAR_SERVER_CANNOT_QUERY | FCVAR_PROTECTED ) )
					continue;

				if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) )
				{
					char var1[ MAX_OSPATH ];
					char var2[ MAX_OSPATH ];

					Q_strncpy( var1, Host_CleanupConVarStringValue( pCvar->GetString() ), sizeof( var1 ) );
					Q_strncpy( var2, Host_CleanupConVarStringValue( pCvar->GetDefault() ), sizeof( var2 ) );

					if ( !Q_stricmp( var1, var2 ) )
						continue;
				}
				else
				{
					if ( pCvar->GetFloat() == Q_atof( pCvar->GetDefault() ) )
						continue;
				}

				if ( !(pCvar->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) )
					CommentPrintf( "%s '%s' '%s'\n", pCvar->GetName(), Host_CleanupConVarStringValue( pCvar->GetString() ), pCvar->GetDefault() );
				else
					CommentPrintf( "%s '%f' '%f'\n", pCvar->GetName(), pCvar->GetFloat(), Q_atof( pCvar->GetDefault() ) );
			}

			CommentCat( "\nConsole History (reversed)\n\n" );

			// Get console
			int len = V_strlen( m_errorText );
			if ( len < sizeof( m_errorText ) )
			{
				GetSpew( m_errorText + len, sizeof( m_errorText ) - len - 1 );
				m_errorText[ sizeof( m_errorText ) - 1 ] = 0;
			}
		}
		catch ( ... )
		{
			CommentCat( "Exception thrown building console/convar history.\n" );
		}
	}

#if defined( LINUX )

	void AddFileToComment( const char *filename )
	{
		CommentPrintf( "\n%s:\n", filename );

		int nStart = Q_strlen( m_errorText );
		int nMaxLen = sizeof( m_errorText ) - nStart - 1;

		if ( nMaxLen > 0 )
		{
			FILE *fh = fopen( filename, "r" );

			if ( fh )
			{
				size_t ret = fread( m_errorText + nStart, 1, nMaxLen, fh );
				fclose( fh );

				// Replace tab characters with spaces.
				for ( size_t i = 0; i < ret; i++ )
				{
					if ( m_errorText[ nStart + i ] == '\t' )
						m_errorText[ nStart + i ] = ' ';
				}
			}

			// Entire buffer should have been zeroed out, but just super sure...
			m_errorText[ sizeof( m_errorText ) - 1 ] = 0;
		}
	}

#endif // LINUX

public:
	char m_errorText[ _SIZE ];
	bool m_bIsDedicatedServer;
};

#if defined( _X360 )
static CErrorText<3500> errorText;
#else
static CErrorText<95000> errorText;
#endif

void BuildMinidumpComment( char const *pchSysErrorText, bool bRealCrash )
{
#if !defined(NO_STEAM)
	/*
	// Uncomment this code if you are testing max minidump comment length issues
	// It allows you to asked for a dummy comment of a certain length
	int nCommentLength = CommandLine()->ParmValue( "-commentlen", 0 );
	if ( nCommentLength > 0 )
	{
		nCommentLength = MIN( nCommentLength, 128*1024 );
		char *cbuf = new char[ nCommentLength + 1 ];
		for ( int i = 0; i < nCommentLength; ++i )
		{
			cbuf[ i ] = (char)('0' + (i % 10));
		}
		cbuf[ nCommentLength ] = 0;
		SteamAPI_SetMiniDumpComment( cbuf );
		delete[] cbuf;
		return;
	}
	*/
	errorText.BuildComment( pchSysErrorText, bRealCrash );
#endif
}

#if defined( POSIX )

static void PosixPreMinidumpCallback( void *context )
{
	BuildMinidumpComment( NULL, true );
}

#endif

//-----------------------------------------------------------------------------
// Purpose: Attempt to initialize appid/steam.inf/minidump information. May only return partial information if called
//          before Filesystem is ready.
//
//          The desire is to be able to call this ASAP to init basic minidump and AppID info, then re-call later on when
//          the filesystem is setup, so full version # information and such can be propagated to the minidump system.
//          (Currently, SDK mods will generally only have partial information prior to filesystem init)
//-----------------------------------------------------------------------------
// steam.inf keys.
#define VERSION_KEY				"PatchVersion="
#define PRODUCT_KEY				"ProductName="
#define SERVER_VERSION_KEY		"ServerVersion="
#define APPID_KEY				"AppID="
#define SERVER_APPID_KEY		"ServerAppID="
enum eSteamInfoInit
{
	eSteamInfo_Uninitialized,
	eSteamInfo_Partial,
	eSteamInfo_Initialized
};
static eSteamInfoInit Sys_TryInitSteamInfo( void *pvAPI, SteamInfVersionInfo_t& VerInfo, const char *pchMod, const char *pchBaseDir, bool bDedicated )
{
	static eSteamInfoInit initState = eSteamInfo_Uninitialized;

	eSteamInfoInit previousInitState = initState;

	//
	//
	// Initialize with some defaults.
	VerInfo.ClientVersion = 0;
	VerInfo.ServerVersion = 0;
	V_strcpy_safe( VerInfo.szVersionString, "valve" );
	V_strcpy_safe( VerInfo.szProductString, "1.0.1.0" );
	VerInfo.AppID = k_uAppIdInvalid;
	VerInfo.ServerAppID = k_uAppIdInvalid;

	// Filesystem may or may not be up
	CUtlBuffer infBuf;
	bool bFoundInf = false;
	if ( g_pFileSystem )
	{
		FileHandle_t fh;
		fh = g_pFileSystem->Open( "steam.inf", "rb", "GAME" );
		bFoundInf = fh && g_pFileSystem->ReadToBuffer( fh, infBuf );
	}

	if ( !bFoundInf )
	{
		// We may try to load the steam.inf BEFORE we turn on the filesystem, so use raw filesystem API's here.
		char szFullPath[ MAX_PATH ] = { 0 };
		char szModSteamInfPath[ MAX_PATH ] = { 0 };
		V_ComposeFileName( pchMod, "steam.inf", szModSteamInfPath, sizeof( szModSteamInfPath ) );
		V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModSteamInfPath, pchBaseDir );

		// Try opening steam.inf
		FILE *fp = fopen( szFullPath, "rb" );
		if ( fp )
		{
			// Read steam.inf data.
			fseek( fp, 0, SEEK_END );
			size_t bufsize = ftell( fp );
			fseek( fp, 0, SEEK_SET );

			infBuf.EnsureCapacity( bufsize + 1 );

			size_t iBytesRead = fread( infBuf.Base(), 1, bufsize, fp );
			((char *)infBuf.Base())[iBytesRead] = 0;
			infBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, iBytesRead + 1 );
			fclose( fp );

			bFoundInf = ( iBytesRead == bufsize );
		}
	}

	if ( bFoundInf )
	{
		const char *pbuf = (const char*)infBuf.Base();
		while ( 1 )
		{
			pbuf = COM_Parse( pbuf );
			if ( !pbuf || !com_token[ 0 ] )
				break;

			if ( !Q_strnicmp( com_token, VERSION_KEY, Q_strlen( VERSION_KEY ) ) )
			{
				V_strcpy_safe( VerInfo.szVersionString, com_token + Q_strlen( VERSION_KEY ) );
				VerInfo.ClientVersion = atoi( VerInfo.szVersionString );
			}
			else if ( !Q_strnicmp( com_token, PRODUCT_KEY, Q_strlen( PRODUCT_KEY ) ) )
			{
				V_strcpy_safe( VerInfo.szProductString, com_token + Q_strlen( PRODUCT_KEY ) );
			}
			else if ( !Q_strnicmp( com_token, SERVER_VERSION_KEY, Q_strlen( SERVER_VERSION_KEY ) ) )
			{
				VerInfo.ServerVersion = atoi( com_token + Q_strlen( SERVER_VERSION_KEY ) );
			}
			else if ( !Q_strnicmp( com_token, APPID_KEY, Q_strlen( APPID_KEY ) ) )
			{
				VerInfo.AppID = atoi( com_token + Q_strlen( APPID_KEY ) );
			}
			else if ( !Q_strnicmp( com_token, SERVER_APPID_KEY, Q_strlen( SERVER_APPID_KEY ) ) )
			{
				VerInfo.ServerAppID = atoi( com_token + Q_strlen( SERVER_APPID_KEY ) );
			}
		}

		// If we found a steam.inf we're as good as we're going to get, but don't tell callers we're fully initialized
		// if it doesn't at least have an AppID
		initState = ( VerInfo.AppID != k_uAppIdInvalid ) ? eSteamInfo_Initialized : eSteamInfo_Partial;
	}
	else if ( !bDedicated )
	{
		// Opening steam.inf failed - try to open gameinfo.txt and read in just SteamAppId from that.
		// (gameinfo.txt lacks the dedicated server steamid, so we'll just have to live until filesystem init to setup
		// breakpad there when we hit this case)
		char szModGameinfoPath[ MAX_PATH ] = { 0 };
		char szFullPath[ MAX_PATH ] = { 0 };
		V_ComposeFileName( pchMod, "gameinfo.txt", szModGameinfoPath, sizeof( szModGameinfoPath ) );
		V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModGameinfoPath, pchBaseDir );

		// Try opening gameinfo.txt
		FILE *fp = fopen( szFullPath, "rb" );
		if( fp )
		{
			fseek( fp, 0, SEEK_END );
			size_t bufsize = ftell( fp );
			fseek( fp, 0, SEEK_SET );

			char *buffer = ( char * )_alloca( bufsize + 1 );

			size_t iBytesRead = fread( buffer, 1, bufsize, fp );
			buffer[ iBytesRead ] = 0;
			fclose( fp );

			KeyValuesAD pkvGameInfo( "gameinfo" );
			if ( pkvGameInfo->LoadFromBuffer( "gameinfo.txt", buffer ) )
			{
				VerInfo.AppID = (AppId_t)pkvGameInfo->GetInt( "FileSystem/SteamAppId", k_uAppIdInvalid );
			}
		}

		initState = eSteamInfo_Partial;
	}

	// In partial state the ServerAppID might be unknown, but if we found the full steam.inf and it's not set, it shares AppID.
	if ( initState == eSteamInfo_Initialized && VerInfo.ServerAppID == k_uAppIdInvalid )
		VerInfo.ServerAppID = VerInfo.AppID;

#if !defined(_X360)
	if ( VerInfo.AppID )
	{
		// steamclient.dll doesn't know about steam.inf files in mod folder,
		// it accepts a steam_appid.txt in the root directory if the game is
		// not started through Steam. So we create one there containing the
		// current AppID
		FILE *fh = fopen( "steam_appid.txt", "wb" );
		if ( fh  )
		{
			CFmtStrN< 128 > strAppID( "%u\n", VerInfo.AppID );

			fwrite( strAppID.Get(), strAppID.Length() + 1, 1, fh );
			fclose( fh );
		}
	}
#endif // !_X360

	//
	// Update minidump info if we have more information than before
	//

#ifndef NO_STEAM
	// If -nobreakpad was specified or we found metamod or sourcemod, don't register breakpad.
	bool bUseBreakpad = !CommandLine()->FindParm( "-nobreakpad" ) && ( !bDedicated || !IsSourceModLoaded() );
	AppId_t BreakpadAppId = bDedicated ? VerInfo.ServerAppID : VerInfo.AppID;
	Assert( BreakpadAppId != k_uAppIdInvalid || initState < eSteamInfo_Initialized );
	if ( BreakpadAppId != k_uAppIdInvalid && initState > previousInitState && bUseBreakpad )
	{
		void *pvMiniDumpContext = NULL;
		PFNPreMinidumpCallback pfnPreMinidumpCallback = NULL;
		bool bFullMemoryDump = !bDedicated && IsWindows() && CommandLine()->FindParm( "-full_memory_dumps" );

#if defined( POSIX )
		// On Windows we're relying on the try/except to build the minidump comment. On Linux, we don't have that
		//	so we need to register the minidumpcallback handler here.
		pvMiniDumpContext = pvAPI;
		pfnPreMinidumpCallback = PosixPreMinidumpCallback;
#endif

		CFmtStrN<128> pchVersion( "%d", build_number() );
		Msg( "Using Breakpad minidump system. Version: %s AppID: %u\n", pchVersion.Get(), BreakpadAppId );

		// We can filter various crash dumps differently in the Socorro backend code:
		//    Steam/min/web/crash_reporter/socorro/scripts/config/collectorconfig.py
		SteamAPI_SetBreakpadAppID( BreakpadAppId );
		SteamAPI_UseBreakpadCrashHandler( pchVersion, __DATE__, __TIME__, bFullMemoryDump, pvMiniDumpContext, pfnPreMinidumpCallback );

		// Tell errorText class if this is dedicated server.
		errorText.m_bIsDedicatedServer = bDedicated;
	}
#endif // NO_STEAM

	MinidumpUserStreamInfoSetHeader( "%sLaunching \"%s\"\n", ( bDedicated ? "DedicatedServerAPI " : "" ), CommandLine()->GetCmdLine() );


	return initState;
}

#ifndef SWDS

//-----------------------------------------------------------------------------
//
// Main engine interface exposed to launcher
//
//-----------------------------------------------------------------------------
class CEngineAPI : public CTier3AppSystem< IEngineAPI >
{
	typedef CTier3AppSystem< IEngineAPI > BaseClass;

public:
	virtual bool Connect( CreateInterfaceFn factory );
	virtual void Disconnect();
	virtual void *QueryInterface( const char *pInterfaceName );
	virtual InitReturnVal_t Init();
	virtual void Shutdown();

	// This function must be called before init
	virtual void SetStartupInfo( StartupInfo_t &info );

	virtual int Run( );

	// Sets the engine to run in a particular editor window
	virtual void SetEngineWindow( void *hWnd );

	// Posts a console command
	virtual void PostConsoleCommand( const char *pConsoleCommand );

	// Are we running the simulation?
	virtual bool IsRunningSimulation( ) const;

	// Start/stop running the simulation
	virtual void ActivateSimulation( bool bActive );

	// Reset the map we're on
	virtual void SetMap( const char *pMapName );

	bool MainLoop();

	int RunListenServer();

private:

	// Hooks a particular mod up to the registry
	void SetRegistryMod( const char *pModName );

	// One-time setup, based on the initially selected mod
	// FIXME: This should move into the launcher!
	bool OnStartup( void *pInstance, const char *pStartupModName );
	void OnShutdown();

	// Initialization, shutdown of a mod.
	bool ModInit( const char *pModName, const char *pGameDir );
	void ModShutdown();

	// Initializes, shuts down the registry
	bool InitRegistry( const char *pModName );
	void ShutdownRegistry();

	// Handles there being an error setting up the video mode
	InitReturnVal_t HandleSetModeError();

	// Initializes, shuts down VR
	bool InitVR();
	void ShutdownVR();

	// Purpose: Message pump when running stand-alone
	void PumpMessages();

	// Purpose: Message pump when running with the editor
	void PumpMessagesEditMode( bool &bIdle, long &lIdleCount );

	// Activate/deactivates edit mode shaders
	void ActivateEditModeShaders( bool bActive );

private:
	void *m_hEditorHWnd;
	bool m_bRunningSimulation;
	bool m_bSupportsVR;
	StartupInfo_t m_StartupInfo;
};


//-----------------------------------------------------------------------------
// Singleton interface
//-----------------------------------------------------------------------------
static CEngineAPI s_EngineAPI;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineAPI, IEngineAPI, VENGINE_LAUNCHER_API_VERSION, s_EngineAPI );


//-----------------------------------------------------------------------------
// Connect, disconnect
//-----------------------------------------------------------------------------
bool CEngineAPI::Connect( CreateInterfaceFn factory ) 
{ 
	// Store off the app system factory...
	g_AppSystemFactory = factory;

	if ( !BaseClass::Connect( factory ) )
		return false;

	g_pFileSystem = g_pFullFileSystem;
	if ( !g_pFileSystem )
		return false;

	g_pFileSystem->SetWarningFunc( Warning );

	if ( !Shader_Connect( true ) )
		return false;

	g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL );

	if ( !g_pStudioRender || !g_pDataCache || !g_pPhysics || !g_pMDLCache || !g_pMatSystemSurface || !g_pInputSystem /* || !g_pVideo */ )
	{
		Warning( "Engine wasn't able to acquire required interfaces!\n" );
		return false;
	}

	if (!g_pStudioRender)
	{
		Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION );
		return false;
	}

	g_pHammer = (IHammer*)factory( INTERFACEVERSION_HAMMER, NULL );

#if defined( USE_SDL )
	g_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
#endif
	
	ConnectMDLCacheNotify();

	return true; 
}

void CEngineAPI::Disconnect() 
{
	DisconnectMDLCacheNotify();

#if !defined( SWDS )
	TRACESHUTDOWN( Steam3Client().Shutdown() );
#endif

	g_pHammer = NULL;
	g_pPhysics = NULL;

	Shader_Disconnect();

	g_pFileSystem = NULL;

	BaseClass::Disconnect();

	g_AppSystemFactory = NULL;
}


//-----------------------------------------------------------------------------
// Query interface
//-----------------------------------------------------------------------------
void *CEngineAPI::QueryInterface( const char *pInterfaceName )
{
	// Loading the engine DLL mounts *all* engine interfaces
	CreateInterfaceFn factory = Sys_GetFactoryThis();	// This silly construction is necessary
	return factory( pInterfaceName, NULL );				// to prevent the LTCG compiler from crashing.
}


//-----------------------------------------------------------------------------
// Sets startup info
//-----------------------------------------------------------------------------
void CEngineAPI::SetStartupInfo( StartupInfo_t &info )
{
	// Setup and write out steam_appid.txt before we launch
	bool bDedicated = false; // Dedicated comes through CDedicatedServerAPI
	eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );

	g_bTextMode = info.m_bTextMode;

	// Set up the engineparms_t which contains global information about the mod
	host_parms.basedir = const_cast<char*>( info.m_pBaseDirectory );

	// Copy off all the startup info
	m_StartupInfo = info;

#if !defined( SWDS )
	// turn on the Steam3 API early so we can query app data up front
	TRACEINIT( Steam3Client().Activate(), Steam3Client().Shutdown() );
#endif

	// Needs to be done prior to init material system config
	TRACEINIT( COM_InitFilesystem( m_StartupInfo.m_pInitialMod ), COM_ShutdownFileSystem() );

	if ( steamInfo != eSteamInfo_Initialized )
	{
		// Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find
		// their steam.inf, due to mounting SDK search paths.
		steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
		Assert( steamInfo == eSteamInfo_Initialized );
		if ( steamInfo != eSteamInfo_Initialized )
		{
			Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" );
		}
	}

	m_bSupportsVR = false;
	if ( IsPC() )
	{
		KeyValues *modinfo = new KeyValues("ModInfo");
		if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
		{
			// Enable file tracking - client always does this in case it connects to a pure server.
			// server only does this if sv_pure is set
			// If it's not singleplayer_only
			if ( V_stricmp( modinfo->GetString("type", "singleplayer_only"), "singleplayer_only") == 0 )
			{
				DevMsg( "Disabling whitelist file tracking in filesystem...\n" );
				g_pFileSystem->EnableWhitelistFileTracking( false, false, false );
			}
			else
			{
				DevMsg( "Enabling whitelist file tracking in filesystem...\n" );
				g_pFileSystem->EnableWhitelistFileTracking( true, false, false );
			}

			m_bSupportsVR = modinfo->GetInt( "supportsvr" ) > 0 && CommandLine()->CheckParm( "-vr" );
			if ( m_bSupportsVR )
			{
				// This also has to happen before CreateGameWindow to know where to put
				// the window and how big to make it
				if ( InitVR() )
				{
					if ( Steam3Client().SteamUtils() )
					{
						if ( Steam3Client().SteamUtils()->IsSteamRunningInVR() && g_pSourceVR->IsHmdConnected() )
						{
							int nForceVRAdapterIndex = g_pSourceVR->GetVRModeAdapter();
							materials->SetAdapter( nForceVRAdapterIndex, 0 );

							g_pSourceVR->SetShouldForceVRMode();
						}
					}
				}
			}

		}
		modinfo->deleteThis();
	}
}


//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CEngineAPI::Init() 
{
	if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 )
	{
		Plat_SetBenchmarkMode( true );
	}

	InitReturnVal_t nRetVal = BaseClass::Init();
	if ( nRetVal != INIT_OK )
		return nRetVal;

	m_bRunningSimulation = false;

	// Initialize the FPU control word
#if defined(WIN32) && !defined( SWDS ) && !defined( _X360 )
	_asm
	{
		fninit
	}
#endif

	SetupFPUControlWord();

	// This creates the videomode singleton object, it doesn't depend on the registry
	VideoMode_Create();

	// Initialize the editor hwnd to render into
	m_hEditorHWnd = NULL;

	// One-time setup
	// FIXME: OnStartup + OnShutdown should be removed + moved into the launcher
	// or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown
	if ( !OnStartup( m_StartupInfo.m_pInstance, m_StartupInfo.m_pInitialMod ) )
	{
		return HandleSetModeError();
	}

	return INIT_OK; 
}

void CEngineAPI::Shutdown() 
{
	VideoMode_Destroy();
	BaseClass::Shutdown();
}


//-----------------------------------------------------------------------------
// Sets the engine to run in a particular editor window
//-----------------------------------------------------------------------------
void CEngineAPI::SetEngineWindow( void *hWnd )
{
	if ( !InEditMode() )
		return;

	// Detach input from the previous editor window
	game->InputDetachFromGameWindow();

	m_hEditorHWnd = hWnd;
	videomode->SetGameWindow( m_hEditorHWnd );
}


//-----------------------------------------------------------------------------
// Posts a console command
//-----------------------------------------------------------------------------
void CEngineAPI::PostConsoleCommand( const char *pCommand )
{
	Cbuf_AddText( pCommand );
}

	
//-----------------------------------------------------------------------------
// Is the engine currently rinning?
//-----------------------------------------------------------------------------
bool CEngineAPI::IsRunningSimulation() const
{
	return (eng->GetState() == IEngine::DLL_ACTIVE);
}


//-----------------------------------------------------------------------------
// Reset the map we're on
//-----------------------------------------------------------------------------
void CEngineAPI::SetMap( const char *pMapName )
{
//	if ( !Q_stricmp( sv.mapname, pMapName ) )
//		return;

	char buf[MAX_PATH];
	Q_snprintf( buf, MAX_PATH, "map %s", pMapName );
	Cbuf_AddText( buf );
}


//-----------------------------------------------------------------------------
// Start/stop running the simulation
//-----------------------------------------------------------------------------
void CEngineAPI::ActivateSimulation( bool bActive )
{
	// FIXME: Not sure what will happen in this case
	if ( ( eng->GetState() != IEngine::DLL_ACTIVE )	&&
		 ( eng->GetState() != IEngine::DLL_PAUSED ) )
	{
		return;
	}

	bool bCurrentlyActive = (eng->GetState() != IEngine::DLL_PAUSED);
	if ( bActive == bCurrentlyActive )
		return;

	// FIXME: Should attachment/detachment be part of the state machine in IEngine?
	if ( !bActive )
	{
		eng->SetNextState( IEngine::DLL_PAUSED );

		// Detach input from the previous editor window
		game->InputDetachFromGameWindow();
	}
	else
	{
		eng->SetNextState( IEngine::DLL_ACTIVE );

		// Start accepting input from the new window
		// FIXME: What if the attachment fails?
		game->InputAttachToGameWindow();
	}
}

static void MoveConsoleWindowToFront()
{
#ifdef _WIN32
	// Move the window to the front.
	HINSTANCE hInst = LoadLibrary( "kernel32.dll" );
	if ( hInst )
	{
		typedef HWND (*GetConsoleWindowFn)();
		GetConsoleWindowFn fn = (GetConsoleWindowFn)GetProcAddress( hInst, "GetConsoleWindow" );
		if ( fn )
		{
			HWND hwnd = fn();
			ShowWindow( hwnd, SW_SHOW );
			UpdateWindow( hwnd );
			SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW );
		}
		FreeLibrary( hInst );
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Message pump when running stand-alone
//-----------------------------------------------------------------------------
void CEngineAPI::PumpMessages()
{
	// This message pumping happens in SDL if SDL is enabled.
#if defined( PLATFORM_WINDOWS ) && !defined( USE_SDL )
	MSG msg;
	while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
	{
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}
#endif

#if defined( USE_SDL )
	g_pLauncherMgr->PumpWindowsMessageLoop();
#endif

	// Get input from attached devices
	g_pInputSystem->PollInputState();

	if ( IsX360() )
	{
		// handle Xbox system messages
		XBX_ProcessEvents();
	}

	// NOTE: Under some implementations of Win9x, 
	// dispatching messages can cause the FPU control word to change
	if ( IsPC() )
	{
		SetupFPUControlWord();
	}

	game->DispatchAllStoredGameMessages();

	if ( IsPC() )
	{
		static bool s_bFirstRun = true;
		if ( s_bFirstRun )
		{
			s_bFirstRun = false;
			MoveConsoleWindowToFront();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Message pump when running stand-alone
//-----------------------------------------------------------------------------
void CEngineAPI::PumpMessagesEditMode( bool &bIdle, long &lIdleCount )
{

	if ( bIdle && !g_pHammer->HammerOnIdle( lIdleCount++ ) )
	{
		bIdle = false;
	}

	// Get input from attached devices
	g_pInputSystem->PollInputState();

#ifdef WIN32
	MSG msg;
	while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
	{
		if ( msg.message == WM_QUIT )
		{
			eng->SetQuitting( IEngine::QUIT_TODESKTOP );
			break;
		}

		if ( !g_pHammer->HammerPreTranslateMessage(&msg) )
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		// Reset idle state after pumping idle message.
		if ( g_pHammer->HammerIsIdleMessage(&msg) )
		{
			bIdle = true;
			lIdleCount = 0;
		}
	}
#elif defined( USE_SDL )
	Error( "Not supported" );
#else
#error
#endif


	// NOTE: Under some implementations of Win9x, 
	// dispatching messages can cause the FPU control word to change
	SetupFPUControlWord();

	game->DispatchAllStoredGameMessages();
}

//-----------------------------------------------------------------------------
// Activate/deactivates edit mode shaders
//-----------------------------------------------------------------------------
void CEngineAPI::ActivateEditModeShaders( bool bActive )
{
	if ( InEditMode() && ( g_pMaterialSystemConfig->bEditMode != bActive ) )
	{
		MaterialSystem_Config_t config = *g_pMaterialSystemConfig;
		config.bEditMode = bActive;
		OverrideMaterialSystemConfig( config );
	}
}


#ifdef GPROFILER
static bool g_gprofiling = false;

CON_COMMAND( gprofilerstart, "Starts the gperftools profiler recording to the specified file." )
{
	if ( g_gprofiling )
	{
		Msg( "Profiling is already started.\n" );
		return;
	}

	char buffer[500];
	const char* profname = buffer;
	if ( args.ArgC() < 2 )
	{
		static const char *s_pszHomeDir = getenv("HOME");
		if ( !s_pszHomeDir )
		{
			Msg( "Syntax: gprofile <outputfilename>\n" );
			return;
		}

		// Use the current date and time to create a unique file name.time_t t = time(NULL);
		time_t t = time(NULL);
		struct tm tm = *localtime(&t);

		V_sprintf_safe( buffer, "%s/valveprofile_%4d_%02d_%02d_%02d.%02d.%02d.prof", s_pszHomeDir,
					tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec );
		// profname already points to buffer.
	}
	else
	{
		profname = args[1];
	}

	int result = ProfilerStart( profname );
	if ( result )
	{
		Msg( "Profiling started successfully. Recording to %s. Stop profiling with gprofilerstop.\n", profname );
		g_gprofiling = true;
	}
	else
	{
		Msg( "Profiling to %s failed to start - errno = %d.\n", profname, errno );
	}
}

CON_COMMAND( gprofilerstop, "Stops the gperftools profiler." )
{
	if ( g_gprofiling )
	{
		ProfilerStop();
		Msg( "Stopped profiling.\n" );
		g_gprofiling = false;
	}
}
#endif


void StopGProfiler()
{
#ifdef GPROFILER
	gprofilerstop( CCommand() );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: Message pump
//-----------------------------------------------------------------------------
bool CEngineAPI::MainLoop()
{
	bool bIdle = true;
	long lIdleCount = 0;

	// Main message pump
	while ( true )
	{
		// Pump messages unless someone wants to quit
		if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING )
		{
			// We have to explicitly stop the profiler since otherwise symbol
			// resolution doesn't work correctly.
			StopGProfiler();
			if ( eng->GetQuitting() != IEngine::QUIT_TODESKTOP )
				return true;
			return false;
		}

		// Pump the message loop
		if ( !InEditMode() )
		{
			PumpMessages();
		}
		else
		{
			PumpMessagesEditMode( bIdle, lIdleCount );
		}

		// Run engine frame + hammer frame
		if ( !InEditMode() || m_hEditorHWnd )
		{
			VCRSyncToken( "Frame" );

		// Deactivate edit mode shaders
		ActivateEditModeShaders( false );

		eng->Frame();

		// Reactivate edit mode shaders (in Edit mode only...)
		ActivateEditModeShaders( true );
		}

		if ( InEditMode() )
		{
			g_pHammer->RunFrame();
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
// Initializes, shuts down the registry
//-----------------------------------------------------------------------------
bool CEngineAPI::InitRegistry( const char *pModName )
{
	if ( IsPC() )
	{
		char szRegSubPath[MAX_PATH];
		Q_snprintf( szRegSubPath, sizeof(szRegSubPath), "%s\\%s", "Source", pModName );
		return registry->Init( szRegSubPath );
	}
	return true;
}

void CEngineAPI::ShutdownRegistry( )
{
	if ( IsPC() )
	{
		registry->Shutdown( );
	}
}


//-----------------------------------------------------------------------------
// Initializes, shuts down VR (via sourcevr.dll)
//-----------------------------------------------------------------------------
bool CEngineAPI::InitVR()
{
	if ( m_bSupportsVR )
	{
		g_pSourceVR = (ISourceVirtualReality *)g_AppSystemFactory( SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL );
		if ( g_pSourceVR )
		{
			// make sure that the sourcevr DLL we loaded is secure. If not, don't 
			// let this client connect to secure servers.
			if ( !Host_AllowLoadModule( "sourcevr" DLL_EXT_STRING, "EXECUTABLE_PATH", false ) )
			{
				Warning( "Preventing connections to secure servers because sourcevr.dll is not signed.\n" );
				Host_DisallowSecureServers();
			}
		}
	}
	return true;
}


void CEngineAPI::ShutdownVR()
{
}


//-----------------------------------------------------------------------------
// One-time setup, based on the initially selected mod
// FIXME: This should move into the launcher!
//-----------------------------------------------------------------------------
bool CEngineAPI::OnStartup( void *pInstance, const char *pStartupModName )
{
	// This fixes a bug on certain machines where the input will 
	// stop coming in for about 1 second when someone hits a key.
	// (true means to disable priority boost)
#ifdef WIN32
	if ( IsPC() )
	{
		SetThreadPriorityBoost( GetCurrentThread(), true ); 
	}
#endif

	// FIXME: Turn videomode + game into IAppSystems?

	// Try to create the window
	COM_TimestampedLog( "game->Init" );

	// This has to happen before CreateGameWindow to set up the instance
	// for use by the code that creates the window
	if ( !game->Init( pInstance ) )
	{
		goto onStartupError;
	}

	// Try to create the window
	COM_TimestampedLog( "videomode->Init" );

	// This needs to be after Shader_Init and registry->Init
	// This way mods can have different default video settings
	if ( !videomode->Init( ) )
	{
		goto onStartupShutdownGame;
	}

	// We need to access the registry to get various settings (specifically,
	// InitMaterialSystemConfig requires it).
	if ( !InitRegistry( pStartupModName ) )
	{
		goto onStartupShutdownVideoMode;
	}

	materials->ModInit();

	// Setup the material system config record, CreateGameWindow depends on it
	// (when we're running stand-alone)
	InitMaterialSystemConfig( InEditMode() );

#if defined( _X360 )
	XBX_NotifyCreateListener( XNOTIFY_SYSTEM|XNOTIFY_LIVE|XNOTIFY_XMP );
#endif

	ShutdownRegistry();
	return true;

	// Various error conditions
onStartupShutdownVideoMode:
	videomode->Shutdown();

onStartupShutdownGame:
	game->Shutdown();

onStartupError:
	return false;
}


//-----------------------------------------------------------------------------
// One-time shutdown (shuts down stuff set up in OnStartup)
// FIXME: This should move into the launcher!
//-----------------------------------------------------------------------------
void CEngineAPI::OnShutdown()
{
	if ( videomode )
	{
		videomode->Shutdown();
	}

	ShutdownVR();

	// Shut down the game
	game->Shutdown();

	materials->ModShutdown();
	TRACESHUTDOWN( COM_ShutdownFileSystem() );
}

static bool IsValveMod( const char *pModName )
{
	// Figure out if we're running a Valve mod or not.
	return ( Q_stricmp( GetCurrentMod(), "cstrike" ) == 0 ||
		Q_stricmp( GetCurrentMod(), "dod" ) == 0 ||
		Q_stricmp( GetCurrentMod(), "hl1mp" ) == 0 ||
		Q_stricmp( GetCurrentMod(), "tf" ) == 0 || 
		Q_stricmp( GetCurrentMod(), "tf_beta" ) == 0 ||
		Q_stricmp( GetCurrentMod(), "hl2mp" ) == 0 );
}

//-----------------------------------------------------------------------------
// Initialization, shutdown of a mod.
//-----------------------------------------------------------------------------
bool CEngineAPI::ModInit( const char *pModName, const char *pGameDir )
{
	// Set up the engineparms_t which contains global information about the mod
	host_parms.mod = COM_StringCopy( GetModDirFromPath( pModName ) );
	host_parms.game = COM_StringCopy( pGameDir );

	// By default, restrict server commands in Valve games and don't restrict them in mods.
	cl.m_bRestrictServerCommands = IsValveMod( host_parms.mod );
	cl.m_bRestrictClientCommands = cl.m_bRestrictServerCommands;

	// build the registry path we're going to use for this mod
	InitRegistry( pModName );

	// This sets up the game search path, depends on host_parms
	TRACEINIT( MapReslistGenerator_Init(), MapReslistGenerator_Shutdown() );
#if !defined( _X360 )
	TRACEINIT( DevShotGenerator_Init(), DevShotGenerator_Shutdown() );
#endif

	// Slam cvars based on mod/config.cfg
	Host_ReadPreStartupConfiguration();

	bool bWindowed = g_pMaterialSystemConfig->Windowed();
	if( g_pMaterialSystemConfig->m_nVRModeAdapter != -1 )
	{
		// at init time we never want to start up full screen
		bWindowed = true;
	}

	// Create the game window now that we have a search path
	// FIXME: Deal with initial window width + height better
	if ( !videomode || !videomode->CreateGameWindow( g_pMaterialSystemConfig->m_VideoMode.m_Width, g_pMaterialSystemConfig->m_VideoMode.m_Height, bWindowed ) )
	{
		return false;
	}

	return true;
}

void CEngineAPI::ModShutdown()
{
	COM_StringFree(host_parms.mod);
	COM_StringFree(host_parms.game);
	
	// Stop accepting input from the window
	game->InputDetachFromGameWindow();

#if !defined( _X360 )
	TRACESHUTDOWN( DevShotGenerator_Shutdown() );
#endif
	TRACESHUTDOWN( MapReslistGenerator_Shutdown() );

	ShutdownRegistry();
}


//-----------------------------------------------------------------------------
// Purpose: Handles there being an error setting up the video mode
// Output : Returns true on if the engine should restart, false if it should quit
//-----------------------------------------------------------------------------
InitReturnVal_t CEngineAPI::HandleSetModeError()
{
	// show an error, see if the user wants to restart
	if ( CommandLine()->FindParm( "-safe" ) )
	{
		Sys_MessageBox( "Failed to set video mode.\n\nThis game has a minimum requirement of DirectX 7.0 compatible hardware.\n", "Video mode error", false );
		return INIT_FAILED;
	}
	
	if ( CommandLine()->FindParm( "-autoconfig" ) )
	{
		if ( Sys_MessageBox( "Failed to set video mode - falling back to safe mode settings.\n\nGame will now restart with the new video settings.", "Video - safe mode fallback", true ))
		{
			CommandLine()->AppendParm( "-safe", NULL );
			return (InitReturnVal_t)INIT_RESTART;
		}
		return INIT_FAILED;
	}

	if ( Sys_MessageBox( "Failed to set video mode - resetting to defaults.\n\nGame will now restart with the new video settings.", "Video mode warning", true ) )
	{
		CommandLine()->AppendParm( "-autoconfig", NULL );
		return (InitReturnVal_t)INIT_RESTART;
	}

	return INIT_FAILED;
}


//-----------------------------------------------------------------------------
// Purpose: Main loop for non-dedicated servers
//-----------------------------------------------------------------------------
int CEngineAPI::RunListenServer()
{
	//
	// NOTE: Systems set up here should depend on the mod 
	// Systems which are mod-independent should be set up in the launcher or Init()
	//

	// Innocent until proven guilty
	int nRunResult = RUN_OK;

	// Happens every time we start up and shut down a mod
	if ( ModInit( m_StartupInfo.m_pInitialMod, m_StartupInfo.m_pInitialGame ) )
	{
		CModAppSystemGroup modAppSystemGroup( false, m_StartupInfo.m_pParentAppSystemGroup );

		// Store off the app system factory...
		g_AppSystemFactory = modAppSystemGroup.GetFactory();

		nRunResult = modAppSystemGroup.Run();

		g_AppSystemFactory = NULL;

		// Shuts down the mod
		ModShutdown();

		// Disconnects from the editor window
		videomode->SetGameWindow( NULL );
	}

	// Closes down things that were set up in OnStartup
	// FIXME: OnStartup + OnShutdown should be removed + moved into the launcher
	// or the launcher code should be merged into the engine into the code in OnStartup/OnShutdown
	OnShutdown();

	return nRunResult;
}

static void StaticRunListenServer( void *arg )
{
	*(int *)arg = s_EngineAPI.RunListenServer();
}



// This function is set as the crash handler for unhandled exceptions and as the minidump
// handler for to be used by all of tier0's crash recording. This function
// adds a game-specific minidump comment and ensures that the SteamAPI function is
// used to save the minidump so that crashes are uploaded. SteamAPI has previously
// been configured to use breakpad by calling SteamAPI_UseBreakpadCrashHandler.
extern "C" void __cdecl WriteSteamMiniDumpWithComment( unsigned int uStructuredExceptionCode,
			struct _EXCEPTION_POINTERS * pExceptionInfo,
			const char *pszFilenameSuffix )
{
	// TODO: dynamically set the minidump comment from contextual info about the crash (i.e current VPROF node)?
#if !defined( NO_STEAM )

	if ( g_bUpdateMinidumpComment )
	{
		BuildMinidumpComment( NULL, true );
	}

	SteamAPI_WriteMiniDump( uStructuredExceptionCode, pExceptionInfo, build_number() );
	// Clear DSound Buffers so the sound doesn't loop while the game shuts down
	try
	{
		S_ClearBuffer();
	}
	catch ( ... )
	{
	}
#endif
} 

 
//-----------------------------------------------------------------------------
// Purpose: Main 
//-----------------------------------------------------------------------------
int CEngineAPI::Run()
{
	if ( CommandLine()->FindParm( "-insecure" ) || CommandLine()->FindParm( "-textmode" ) )
	{
		Host_DisallowSecureServers();
	}

#ifdef _X360
	return RunListenServer(); // don't handle exceptions on 360 (because if we do then minidumps won't work at all)
#elif defined ( _WIN32 )
	// Ensure that we crash when we do something naughty in a callback
	// such as a window proc. Otherwise on a 64-bit OS the crashes will be
	// silently swallowed.
	EnableCrashingOnCrashes();

	// Set the default minidump handling function. This is necessary so that Steam
	// will upload crashes, with comments.
	SetMiniDumpFunction( WriteSteamMiniDumpWithComment );

	// Catch unhandled crashes. A normal __try/__except block will not work across
	// the kernel callback boundary, but this does. To be clear, __try/__except
	// and try/catch will usually not catch exceptions in a WindowProc or other
	// callback that is called from kernel mode because 64-bit Windows cannot handle
	// throwing exceptions across that boundary. See this article for details:
	// http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
	// Note that the unhandled exception function is not called when running
	// under a debugger, but that's fine because in that case we don't care about
	// recording minidumps.
	// The try/catch block still makes sense because it is a more reliable way
	// of catching exceptions that aren't in callbacks.
	// The unhandled exception filter will also catch crashes in threads that
	// don't have a try/catch or __try/__except block.
	bool noMinidumps = CommandLine()->FindParm( "-nominidumps");
	if ( !noMinidumps )
		MinidumpSetUnhandledExceptionFunction( WriteSteamMiniDumpWithComment );

	if ( !Plat_IsInDebugSession() && !noMinidumps )
	{
		int nRetVal = RUN_OK;
		CatchAndWriteMiniDumpForVoidPtrFn( StaticRunListenServer, &nRetVal, true );
		return nRetVal;
	}
	else
	{
		return RunListenServer();
	}
#else
	return RunListenServer();
#endif
}
#endif // SWDS

bool g_bUsingLegacyAppSystems = false;

bool CModAppSystemGroup::AddLegacySystems()
{
	g_bUsingLegacyAppSystems = true;

	AppSystemInfo_t appSystems[] = 
	{
		{ "soundemittersystem", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
		{ "", "" }					// Required to terminate the list
	};

	if ( !AddSystems( appSystems ) ) 
		return false;

#if !defined( DEDICATED )
//	if ( CommandLine()->FindParm( "-tools" ) )
	{
		AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING );

		if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) )
			return false;
	}
#endif

	return true;
}

//-----------------------------------------------------------------------------
// Instantiate all main libraries
//-----------------------------------------------------------------------------
bool CModAppSystemGroup::Create()
{
#ifndef SWDS
	if ( !IsServerOnly() )
{
		if ( !ClientDLL_Load() )
	return false;
}
#endif 

	if ( !ServerDLL_Load( IsServerOnly() ) )
		return false;

	IClientDLLSharedAppSystems *clientSharedSystems = 0;

#ifndef SWDS
	if ( !IsServerOnly() )
	{
		clientSharedSystems = ( IClientDLLSharedAppSystems * )g_ClientFactory( CLIENT_DLL_SHARED_APPSYSTEMS, NULL );
		if ( !clientSharedSystems )
			return AddLegacySystems();
	}
#endif

		IServerDLLSharedAppSystems *serverSharedSystems = ( IServerDLLSharedAppSystems * )g_ServerFactory( SERVER_DLL_SHARED_APPSYSTEMS, NULL );
		if ( !serverSharedSystems )
		{
			Assert( !"Expected both game and client .dlls to have or not have shared app systems interfaces!!!" );
			return AddLegacySystems();
		}
	
	// Load game and client .dlls and build list then
	CUtlVector< AppSystemInfo_t >	systems;

	int i;
		int serverCount = serverSharedSystems->Count();
	for ( i = 0 ; i < serverCount; ++i )
		{
			const char *dllName = serverSharedSystems->GetDllName( i );
			const char *interfaceName = serverSharedSystems->GetInterfaceName( i );
	
			AppSystemInfo_t info;
			info.m_pModuleName = dllName;
			info.m_pInterfaceName = interfaceName;
	
			systems.AddToTail( info );
		}

	if ( !IsServerOnly() )
	{
		int clientCount = clientSharedSystems->Count();
		for ( i = 0 ; i < clientCount; ++i )
		{
			const char *dllName = clientSharedSystems->GetDllName( i );
			const char *interfaceName = clientSharedSystems->GetInterfaceName( i );

			if ( ModuleAlreadyInList( systems, dllName, interfaceName ) )
				continue;

			AppSystemInfo_t info;
			info.m_pModuleName = dllName;
			info.m_pInterfaceName = interfaceName;

			systems.AddToTail( info );
		}
	}

	AppSystemInfo_t info;
	info.m_pModuleName = "";
	info.m_pInterfaceName = "";
	systems.AddToTail( info );

	if ( !AddSystems( systems.Base() ) ) 
		return false;

#if !defined( DEDICATED )
//	if ( CommandLine()->FindParm( "-tools" ) )
	{
		AppModule_t toolFrameworkModule = LoadModule( "engine" DLL_EXT_STRING );

		if ( !AddSystem( toolFrameworkModule, VTOOLFRAMEWORK_INTERFACE_VERSION ) )
			return false;
	}
#endif

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Fixme, we might need to verify if the interface names differ for the client versus the server
// Input  : list - 
//			*moduleName - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CModAppSystemGroup::ModuleAlreadyInList( CUtlVector< AppSystemInfo_t >& list, const char *moduleName, const char *interfaceName )
{
	for ( int i = 0; i < list.Count(); ++i )
	{
		if ( !Q_stricmp( list[ i ].m_pModuleName, moduleName ) )
		{
			if ( Q_stricmp( list[ i ].m_pInterfaceName, interfaceName ) )
			{
				Error( "Game and client .dlls requesting different versions '%s' vs. '%s' from '%s'\n",
					list[ i ].m_pInterfaceName, interfaceName, moduleName );
			}
			return true;
		}
	}

	return false;
}

bool CModAppSystemGroup::PreInit()
{
	return true;
}

void SV_ShutdownGameDLL();
int CModAppSystemGroup::Main()
{
	int nRunResult = RUN_OK;

	if ( IsServerOnly() )
	{
		// Start up the game engine
		if ( eng->Load( true, host_parms.basedir ) )
		{
			// If we're using STEAM, pass the map cycle list as resource hints...
			// Dedicated server drives frame loop manually
			dedicated->RunServer();

			SV_ShutdownGameDLL();
		}
	}
	else
	{
		eng->SetQuitting( IEngine::QUIT_NOTQUITTING );

		COM_TimestampedLog( "eng->Load" );

		// Start up the game engine
		static const char engineLoadMessage[] = "Calling CEngine::Load";
		int64 nStartTime = ETWBegin( engineLoadMessage );
		if ( eng->Load( false, host_parms.basedir ) )					
		{
#if !defined(SWDS)
			ETWEnd( engineLoadMessage, nStartTime );
			toolframework->ServerInit( g_ServerFactory );

			if ( s_EngineAPI.MainLoop() )
			{
				nRunResult = RUN_RESTART;
			}

			// unload systems
			eng->Unload();

			toolframework->ServerShutdown();
#endif
			SV_ShutdownGameDLL();
		}
	}
	
	return nRunResult;
}

void CModAppSystemGroup::PostShutdown()
{
}

void CModAppSystemGroup::Destroy() 
{
	// unload game and client .dlls
	ServerDLL_Unload();
#ifndef SWDS
	if ( !IsServerOnly() )
	{
		ClientDLL_Unload();
	}
#endif
}

//-----------------------------------------------------------------------------
//
// Purpose: Expose engine interface to launcher	for dedicated servers
//
//-----------------------------------------------------------------------------
class CDedicatedServerAPI : public CTier3AppSystem< IDedicatedServerAPI >
{
	typedef CTier3AppSystem< IDedicatedServerAPI > BaseClass;

public:
	CDedicatedServerAPI() :
	  m_pDedicatedServer( 0 )
	{
	}
	virtual bool Connect( CreateInterfaceFn factory );
	virtual void Disconnect();
	virtual void *QueryInterface( const char *pInterfaceName );

	virtual bool ModInit( ModInfo_t &info );
	virtual void ModShutdown( void );

	virtual bool RunFrame( void );

	virtual void AddConsoleText( char *text );
	virtual void UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen );
	virtual void UpdateHostname(char *pszHostname, int maxlen);

	CModAppSystemGroup *m_pDedicatedServer;
};

//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
EXPOSE_SINGLE_INTERFACE( CDedicatedServerAPI, IDedicatedServerAPI, VENGINE_HLDS_API_VERSION );

#define LONG_TICK_TIME					0.12f // about 8/66ths of a second
#define MIN_TIME_BETWEEN_DUMPED_TICKS	5.0f;
#define MAX_DUMPS_PER_LONG_TICK			10
void Sys_Sleep ( int msec );

bool g_bLongTickWatcherThreadEnabled = false;
bool g_bQuitLongTickWatcherThread = false;
int g_bTotalDumps = 0;

DWORD __stdcall LongTickWatcherThread( void *voidPtr )
{
	int nLastTick = 0;
	double flWarnTickTime = 0.0f;
	double flNextPossibleDumpTime = Plat_FloatTime() + MIN_TIME_BETWEEN_DUMPED_TICKS;
	int nNumDumpsThisTick = 0;

	while ( eng->GetQuitting() == IEngine::QUIT_NOTQUITTING && !g_bQuitLongTickWatcherThread )
	{
		if ( sv.m_State == ss_active && sv.m_bSimulatingTicks )
		{
			int curTick = sv.m_nTickCount;
			double curTime = Plat_FloatTime();
			if ( nLastTick > 0 && nLastTick == curTick )
			{
				if ( curTime > flNextPossibleDumpTime && curTime > flWarnTickTime && nNumDumpsThisTick < MAX_DUMPS_PER_LONG_TICK )
				{
					nNumDumpsThisTick++;
					g_bTotalDumps++;
					Warning( "Long tick after tick %i. Writing minidump #%i (%i total).\n", nLastTick, nNumDumpsThisTick, g_bTotalDumps );

					if ( nNumDumpsThisTick == MAX_DUMPS_PER_LONG_TICK )
					{
						Msg( "Not writing any more minidumps for this tick.\n" );
					}

					// If you're debugging a minidump and you ended up here, you probably want to switch to the main thread.
					WriteMiniDump( "longtick" );
				}
			}

			if ( nLastTick != curTick )
			{
				if ( nNumDumpsThisTick )
				{
					Msg( "Long tick lasted about %.1f seconds.\n", curTime - (flWarnTickTime - LONG_TICK_TIME) );
					nNumDumpsThisTick = 0;
					flNextPossibleDumpTime = curTime + MIN_TIME_BETWEEN_DUMPED_TICKS;
				}

				nLastTick = curTick;
				flWarnTickTime = curTime + LONG_TICK_TIME;
			}
		}
		else
		{
			nLastTick = 0;
		}

		if ( nNumDumpsThisTick )
		{
			// We'll write the next minidump 0.06 seconds from now.
			Sys_Sleep( 60 );
		}
		else
		{
			// Check tick progress every 1/100th of a second.
			Sys_Sleep( 10 );
		}
	}

	g_bLongTickWatcherThreadEnabled = false;
	g_bQuitLongTickWatcherThread = false;

	return 0;
}

bool EnableLongTickWatcher()
{
	bool bRet = false;
	if ( !g_bLongTickWatcherThreadEnabled )
	{
		g_bQuitLongTickWatcherThread = false;
		g_bLongTickWatcherThreadEnabled = true;

		DWORD nThreadID;
		VCRHook_CreateThread(NULL, 0,
#ifdef POSIX
			(void*)
#endif
			LongTickWatcherThread, NULL, 0, (unsigned long int *)&nThreadID );

		bRet = true;
	}
	else if ( g_bQuitLongTickWatcherThread )
	{
		Msg( "Cannot create a new long tick watcher while waiting for an old one to terminate.\n" );
	}
	else
	{
		Msg( "The long tick watcher thread is already running.\n" );
	}

	return bRet;
}

//-----------------------------------------------------------------------------
// Dedicated server entrypoint
//-----------------------------------------------------------------------------
bool CDedicatedServerAPI::Connect( CreateInterfaceFn factory ) 
{ 
	if ( CommandLine()->FindParm( "-sv_benchmark" ) != 0 )
	{
		Plat_SetBenchmarkMode( true );
	}

	if ( CommandLine()->FindParm( "-dumplongticks" ) )
	{
		Msg( "-dumplongticks found on command line. Activating long tick watcher thread.\n" );
		EnableLongTickWatcher();
	}

	// Store off the app system factory...
	g_AppSystemFactory = factory;

	if ( !BaseClass::Connect( factory ) )
		return false;

	dedicated = ( IDedicatedExports * )factory( VENGINE_DEDICATEDEXPORTS_API_VERSION, NULL );
	if ( !dedicated )
		return false;

	g_pFileSystem = g_pFullFileSystem;
	g_pFileSystem->SetWarningFunc( Warning );

	if ( !Shader_Connect( false ) )
		return false;

	if ( !g_pStudioRender )
	{
		Sys_Error( "Unable to init studio render system version %s\n", STUDIO_RENDER_INTERFACE_VERSION );
		return false;
	}

	g_pPhysics = (IPhysics*)factory( VPHYSICS_INTERFACE_VERSION, NULL );

	if ( !g_pDataCache || !g_pPhysics || !g_pMDLCache )
	{
		Warning( "Engine wasn't able to acquire required interfaces!\n" );
		return false;
	}

	ConnectMDLCacheNotify();
	return true; 
}

void CDedicatedServerAPI::Disconnect() 
{
	DisconnectMDLCacheNotify();

	g_pPhysics = NULL;

	Shader_Disconnect();

	g_pFileSystem = NULL;

	ConVar_Unregister();

	dedicated = NULL;

	BaseClass::Disconnect();

	g_AppSystemFactory = NULL;
}

//-----------------------------------------------------------------------------
// Query interface
//-----------------------------------------------------------------------------
void *CDedicatedServerAPI::QueryInterface( const char *pInterfaceName )
{
	// Loading the engine DLL mounts *all* engine interfaces
	CreateInterfaceFn factory = Sys_GetFactoryThis();	// This silly construction is necessary
	return factory( pInterfaceName, NULL );				// to prevent the LTCG compiler from crashing.
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : type - 0 == normal, 1 == dedicated server
//			*instance - 
//			*basedir - 
//			*cmdline - 
//			launcherFactory - 
//-----------------------------------------------------------------------------
bool CDedicatedServerAPI::ModInit( ModInfo_t &info )
{
	// Setup and write out steam_appid.txt before we launch
	bool bDedicated = true;
	eSteamInfoInit steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );

	eng->SetQuitting( IEngine::QUIT_NOTQUITTING );

	// Set up the engineparms_t which contains global information about the mod
	host_parms.basedir = const_cast<char*>(info.m_pBaseDirectory);
	host_parms.mod = const_cast<char*>(GetModDirFromPath(info.m_pInitialMod));
	host_parms.game = const_cast<char*>(info.m_pInitialGame);

	g_bTextMode = info.m_bTextMode;

	TRACEINIT( COM_InitFilesystem( info.m_pInitialMod ), COM_ShutdownFileSystem() );

	if ( steamInfo != eSteamInfo_Initialized )
	{
		// Try again with filesystem available. This is commonly needed for SDK mods which need the filesystem to find
		// their steam.inf, due to mounting SDK search paths.
		steamInfo = Sys_TryInitSteamInfo( this, g_SteamInfIDVersionInfo, info.m_pInitialMod, info.m_pBaseDirectory, bDedicated );
		Assert( steamInfo == eSteamInfo_Initialized );
		if ( steamInfo != eSteamInfo_Initialized )
		{
			Warning( "Failed to find steam.inf or equivalent steam info. May not have proper information to connect to Steam.\n" );
		}
	}

	// set this up as early as possible, if the server isn't going to run pure, stop CRCing bits as we load them
	// this happens even before the ConCommand's are processed, but we need to be sure to either CRC every file
	// that is loaded, or not bother doing any
	// Note that this mirrors g_sv_pure_mode from sv_main.cpp
	int pure_mode = 1; // default to on, +sv_pure 0 or -sv_pure 0 will turn it off
	if ( CommandLine()->CheckParm("+sv_pure") )
		pure_mode = CommandLine()->ParmValue( "+sv_pure", 1 );
	else if ( CommandLine()->CheckParm("-sv_pure") )
		pure_mode = CommandLine()->ParmValue( "-sv_pure", 1 );
	if ( pure_mode )
		g_pFullFileSystem->EnableWhitelistFileTracking( true, true, CommandLine()->FindParm( "-sv_pure_verify_hashes" ) ? true : false );
	else
		g_pFullFileSystem->EnableWhitelistFileTracking( false, false, false );

	materials->ModInit();

	// Setup the material system config record, CreateGameWindow depends on it
	// (when we're running stand-alone)
#ifndef SWDS
	InitMaterialSystemConfig( true );						// !!should this be called standalone or not?
#endif

	// Initialize general game stuff and create the main window
	if ( game->Init( NULL ) )
	{
		m_pDedicatedServer = new CModAppSystemGroup( true, info.m_pParentAppSystemGroup );

		// Store off the app system factory...
		g_AppSystemFactory = m_pDedicatedServer->GetFactory();

		m_pDedicatedServer->Run();
		return true;
	}

	return false;
}

void CDedicatedServerAPI::ModShutdown( void )
{
	if ( m_pDedicatedServer )
	{
		delete m_pDedicatedServer;
		m_pDedicatedServer = NULL;
	}

	g_AppSystemFactory = NULL;

	// Unload GL, Sound, etc.
	eng->Unload();

	// Shut down memory, etc.
	game->Shutdown();

	materials->ModShutdown();
	TRACESHUTDOWN( COM_ShutdownFileSystem() );
}

bool CDedicatedServerAPI::RunFrame( void )
{
	// Bail if someone wants to quit.
	if ( eng->GetQuitting() != IEngine::QUIT_NOTQUITTING )
	{
		return false;
	}

	// Run engine frame
	eng->Frame();
	return true;
}

void CDedicatedServerAPI::AddConsoleText( char *text )
{
	Cbuf_AddText( text );
}

void CDedicatedServerAPI::UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen )
{
	Host_GetHostInfo( fps, nActive, nMaxPlayers, pszMap, maxlen );
}

void CDedicatedServerAPI::UpdateHostname(char *pszHostname, int maxlen)
{
	if ( pszHostname && ( maxlen > 0 ) )
	{
		Q_strncpy( pszHostname, sv.GetName(), maxlen );
	}
}

#ifndef SWDS

class CGameUIFuncs : public IGameUIFuncs
{
public:
	bool IsKeyDown( const char *keyname, bool& isdown )
	{
		isdown = false;
		if ( !g_ClientDLL )
			return false;

		return g_ClientDLL->IN_IsKeyDown( keyname, isdown );
	}

	const char	*GetBindingForButtonCode( ButtonCode_t code )
	{
		return ::Key_BindingForKey( code );
	}

	virtual ButtonCode_t GetButtonCodeForBind( const char *bind )
	{
		const char *pKeyName = Key_NameForBinding( bind );
		if ( !pKeyName )
			return KEY_NONE;
		return g_pInputSystem->StringToButtonCode( pKeyName ) ;
	}

	void GetVideoModes( struct vmode_s **ppListStart, int *pCount )
	{
		if ( videomode )
		{
			*pCount = videomode->GetModeCount();
			*ppListStart = videomode->GetMode( 0 );
		}
		else
		{
			*pCount = 0;
			*ppListStart = NULL;
		}
	}

	void GetDesktopResolution( int &width, int &height )
	{
		int refreshrate;
		game->GetDesktopInfo( width, height, refreshrate );
	}

	virtual void SetFriendsID( uint friendsID, const char *friendsName )
	{
		cl.SetFriendsID( friendsID, friendsName );
	}

	bool IsConnectedToVACSecureServer()
	{
		if ( cl.IsConnected() )
			return Steam3Client().BGSSecure();
		return false;
	}
};

EXPOSE_SINGLE_INTERFACE( CGameUIFuncs, IGameUIFuncs, VENGINE_GAMEUIFUNCS_VERSION );

#endif

CON_COMMAND( dumplongticks, "Enables generating minidumps on long ticks." )
{
	int enable = atoi( args[1] );
	if ( args.ArgC() == 1 || enable )
	{
		if ( EnableLongTickWatcher() )
		{
			Msg( "Long tick watcher thread created. Use \"dumplongticks 0\" to disable.\n" );
		}
	}
	else
	{
		// disable watcher thread if enabled
		if ( g_bLongTickWatcherThreadEnabled && !g_bQuitLongTickWatcherThread )
		{
			Msg( "Disabling the long tick watcher.\n" );
			g_bQuitLongTickWatcherThread = true;
		}
		else
		{
			Msg( "The long tick watcher is already disabled.\n" );
		}
	}
}