//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//===========================================================================//

#include "datamap.h"
#include "dbg.h"
#include "tier0/fasttimer.h"

#ifdef _WIN32
#include "tier0/memdbgon.h" // needed because in release builds crtdbg.h is handled specially if USE_MEM_DEBUG is defined
#include "tier0/memdbgoff.h"
#include <crtdbg.h>   // For getting at current heap size
#endif

#include "tier1/fmtstr.h"
#include "vstdlib/jobthread.h"

#ifdef USE_SDL
	#include "appframework/ilaunchermgr.h"
	extern ILauncherMgr *g_pLauncherMgr;
#endif

#include "server.h"
#include "host_jmp.h"
#include "screen.h"
#include "keys.h"
#include "cdll_int.h"
#include "eiface.h"
#include "sv_main.h"
#include "sv_log.h"
#include "shadowmgr.h"
#include "zone.h"
#include "gl_cvars.h"
#include "sv_filter.h"
#include "ivideomode.h"
#include "vprof_engine.h"
#include "iengine.h"
#include "tier2/tier2.h"
#include "enginethreads.h"
#include "steam/steam_api.h"
#include "LoadScreenUpdate.h"
#include "datacache/idatacache.h"
#include "master.h"

#if !defined SWDS
#include "voice.h"
#include "sound.h"
#endif

#include "icvar.h"

#include "sys.h"
#include "client.h"
#include "cl_pred.h"
#include "console.h"
#include "view.h"
#include "host.h"
#include "decal.h"
#include "gl_matsysiface.h"
#include "gl_shader.h"
#include "sys_dll.h"
#include "cmodel_engine.h"
#ifndef SWDS
#include "con_nprint.h"
#endif
#include "filesystem.h"
#include "filesystem_engine.h"
#include "tier0/etwprof.h"
#include "tier0/vcrmode.h"
#include "traceinit.h"
#include "host_saverestore.h"
#include "l_studio.h"
#include "cl_demo.h"
#include "cdll_engine_int.h"
#include "host_cmd.h"
#include "host_state.h"
#include "dt_instrumentation.h"
#include "dt_instrumentation_server.h"
#include "const.h"
#include "bitbuf_errorhandler.h"
#include "soundflags.h"
#include "enginestats.h"
#include "tier1/strtools.h"
#include "testscriptmgr.h"
#include "tmessage.h"
#include "tier0/vprof.h"
#include "tier0/icommandline.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "MapReslistGenerator.h"
#include "DownloadListGenerator.h"
#include "download.h"
#include "staticpropmgr.h"
#include "GameEventManager.h"
#include "iprediction.h"
#include "netmessages.h"
#include "cl_main.h"
#include "hltvserver.h"
#include "hltvtest.h"
#if defined( REPLAY_ENABLED )
#include "replayserver.h"
#include "replay_internal.h"
#endif
#include "sys_mainwind.h"
#include "host_phonehome.h"
#ifndef SWDS
#include "vgui_baseui_interface.h"
#include "cl_steamauth.h"
#endif
#include "sv_remoteaccess.h" // NotifyDedicatedServerUI()
#include "snd_audio_source.h"
#include "sv_steamauth.h"
#include "MapReslistGenerator.h"
#include "DevShotGenerator.h"
#include "sv_plugin.h"
#include "toolframework/itoolframework.h"
#include "ienginetoolinternal.h"
#include "inputsystem/iinputsystem.h"
#include "vgui_askconnectpanel.h"
#include "cvar.h"
#include "saverestoretypes.h"
#include "filesystem/IQueuedLoader.h"
#include "soundservice.h"
#include "profile.h"
#include "steam/isteamremotestorage.h"
#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#include "audio_pch.h"
#endif
#if defined( LINUX )
#include <locale.h>

#ifdef USE_SDL
#include "SDL.h"
#endif
#endif

#include "ixboxsystem.h"
extern IXboxSystem *g_pXboxSystem;

extern ConVar cl_cloud_settings;
extern ConVar cl_logofile;

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

//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
void CL_SetPagedPoolInfo();
extern char	*CM_EntityString( void );
extern ConVar host_map;
extern ConVar sv_cheats;

bool g_bDedicatedServerBenchmarkMode = false;
bool g_bAllowSecureServers = true;
bool g_bLowViolence = false;

// These counters are for debugging in dumps.  If these are non-zero it may indicate some kind of 
// heap problem caused by the setjmp/longjmp error handling
int g_HostServerAbortCount = 0;
int g_HostErrorCount = 0;
int g_HostEndDemo = 0;

char g_szDefaultLogoFileName[] = "materials/vgui/logos/spray.vtf";

int host_frameticks = 0;
int host_tickcount = 0;
int host_currentframetick = 0;

static const char g_pModuleExtension[] = DLL_EXT_STRING;

// Engine player info, no game related infos here
BEGIN_BYTESWAP_DATADESC( player_info_s )
	DEFINE_ARRAY( name, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ),
	DEFINE_FIELD( userID, FIELD_INTEGER ),
	DEFINE_ARRAY( guid, FIELD_CHARACTER, SIGNED_GUID_LEN + 1 ),
	DEFINE_FIELD( friendsID, FIELD_INTEGER ),
	DEFINE_ARRAY( friendsName, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ),
	DEFINE_FIELD( fakeplayer, FIELD_BOOLEAN ),
	DEFINE_FIELD( ishltv, FIELD_BOOLEAN ),
#if defined( REPLAY_ENABLED )
	DEFINE_FIELD( isreplay, FIELD_BOOLEAN ),
#endif
	DEFINE_ARRAY( customFiles, FIELD_INTEGER, MAX_CUSTOM_FILES ),
	DEFINE_FIELD( filesDownloaded, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()

//------------------------------------------
enum
{
	FRAME_SEGMENT_INPUT = 0,
	FRAME_SEGMENT_CLIENT,
	FRAME_SEGMENT_SERVER,
	FRAME_SEGMENT_RENDER,
	FRAME_SEGMENT_SOUND,
	FRAME_SEGMENT_CLDLL,
	FRAME_SEGMENT_CMD_EXECUTE,

	NUM_FRAME_SEGMENTS,
};

class CFrameTimer
{
public:
	void ResetDeltas();

	CFrameTimer() : swaptime(0)
	{
		ResetDeltas();
	}

	void MarkFrame();
	void StartFrameSegment( int i )
	{
		starttime[i] = Sys_FloatTime();
	}

	void EndFrameSegment( int i )
	{
		double dt = Sys_FloatTime() - starttime[i];
		deltas[ i ] += dt;
	}
	void MarkSwapTime( )
	{
		double newswaptime = Sys_FloatTime();
		frametime = newswaptime - swaptime;
		swaptime = newswaptime;

		ComputeFrameVariability();
		g_EngineStats.SetFrameTime( frametime );
		g_EngineStats.SetFPSVariability( m_flFPSVariability );

		host_frametime_stddeviation = m_flFPSStdDeviationSeconds;
	}

private:
	enum
	{
		FRAME_HISTORY_COUNT = 50		
	};

	friend void Host_Speeds();
	void ComputeFrameVariability();

	double time_base;
	double times[9];
	double swaptime;
	double frametime;
	double m_flFPSVariability;
	double m_flFPSStdDeviationSeconds;
	double starttime[NUM_FRAME_SEGMENTS];
	double deltas[NUM_FRAME_SEGMENTS];

	float m_pFrameTimeHistory[FRAME_HISTORY_COUNT];
	int m_nFrameTimeHistoryIndex;
};


static CFrameTimer g_HostTimes;


//------------------------------------------


float host_time = 0.0;

static ConVar	violence_hblood( "violence_hblood","1", 0, "Draw human blood" );
static ConVar	violence_hgibs( "violence_hgibs","1", 0, "Show human gib entities" );
static ConVar	violence_ablood( "violence_ablood","1", 0, "Draw alien blood" );
static ConVar	violence_agibs( "violence_agibs","1", 0, "Show alien gib entities" );

// Marked as FCVAR_USERINFO so that the server can cull CC messages before networking them down to us!!!
ConVar closecaption( "closecaption", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX | FCVAR_USERINFO, "Enable close captioning." );
extern ConVar sv_unlockedchapters;

void Snd_Restart_f()
{
#ifndef SWDS
	extern bool snd_firsttime;

	char szVoiceCodec[_MAX_PATH] = { 0 };
	int nVoiceSampleRate = Voice_ConfiguredSampleRate();

	{
		// This is not valid after voice shuts down
		const char *pPreviousCodec = Voice_ConfiguredCodec();
		if ( pPreviousCodec && *pPreviousCodec )
		{
			V_strncpy( szVoiceCodec, pPreviousCodec, sizeof( szVoiceCodec ) );
		}
	}

	S_Shutdown();
	snd_firsttime = true;
	cl.ClearSounds();
	S_Init();

	// Restart voice if it was running
	if ( szVoiceCodec[0] )
		Voice_Init( szVoiceCodec, nVoiceSampleRate );

	// Do this or else it won't have anything in the cache.
	if ( audiosourcecache && sv.GetMapName()[0] )
	{
		audiosourcecache->LevelInit( sv.GetMapName() );
	}

	// Flush soundscapes so they don't stop. We don't insert text in the buffer here because 
	// cl_soundscape_flush is normally cheat-protected.
	ConCommand *pCommand = (ConCommand*)dynamic_cast< const ConCommand* >( g_pCVar->FindCommand( "cl_soundscape_flush" ) );
	if ( pCommand )
	{
		char const *argv[ 1 ] = { "cl_soundscape_flush" };

		CCommand cmd( 1, argv );
		pCommand->Dispatch( cmd );
	}
#endif
}

static ConCommand snd_restart( "snd_restart", Snd_Restart_f, "Restart sound system." );

// In other C files.
void Shader_Shutdown( void );
void R_Shutdown( void );

bool g_bAbortServerSet = false;

#ifdef _WIN32
static bool s_bInitPME = false;
#endif

CON_COMMAND( mem_dump, "Dump memory stats to text file." )
{
	ConMsg("Writing memory stats to file memstats.txt\n");

#if defined( _MEMTEST )
	const char *pTest = sv.GetMapName();
	if ( !pTest || !pTest[0] )
	{
		// possibly at menu
		pTest = "unknown";
	}
	MemAlloc_SetStatsExtraInfo( pTest,"" );
#endif
	MemAlloc_DumpStats();
}

CON_COMMAND( mem_compact, "" )
{
	MemAlloc_CompactHeap();
}

CON_COMMAND( mem_eat, "" )
{
	MemAlloc_Alloc( 1024* 1024 );
}

CON_COMMAND( mem_test, "" )
{
	MemAlloc_CrtCheckMemory();
}

static ConVar host_competitive_ever_enabled( "host_competitive_ever_enabled", "0", FCVAR_HIDDEN, "Has competitive ever been enabled this run?", true, 0, true, 1, true, 1, false, 1, NULL  );

static ConVar mem_test_each_frame( "mem_test_each_frame", "0", 0, "Run heap check at end of every frame\n" );
static ConVar mem_test_every_n_seconds( "mem_test_every_n_seconds", "0", 0, "Run heap check at a specified interval\n" );

static ConVar	singlestep( "singlestep", "0", FCVAR_CHEAT, "Run engine in single step mode ( set next to 1 to advance a frame )" );
static ConVar	cvarNext( "next", "0", FCVAR_CHEAT, "Set to 1 to advance to next frame ( when singlestep == 1 )" );
// Print a debug message when the client or server cache is missed
ConVar host_showcachemiss( "host_showcachemiss", "0", 0, "Print a debug message when the client or server cache is missed." );
static ConVar mem_dumpstats( "mem_dumpstats", "0", 0, "Dump current and max heap usage info to console at end of frame ( set to 2 for continuous output )\n" );
static ConVar host_ShowIPCCallCount( "host_ShowIPCCallCount", "0", 0, "Print # of IPC calls this number of times per second. If set to -1, the # of IPC calls is shown every frame." );

#if defined( RAD_TELEMETRY_ENABLED )
static void OnChangeTelemetryPause ( IConVar *var, const char *pOldValue, float flOldValue )
{
	tmPause( TELEMETRY_LEVEL0, 1 );
}

static void OnChangeTelemetryResume ( IConVar *var, const char *pOldValue, float flOldValue )
{
	tmPause( TELEMETRY_LEVEL0, 0 );
}

static void OnChangeTelemetryLevel ( IConVar *var, const char *pOldValue, float flOldValue )
{
	char* pIEnd;
	const char *pLevel = (( ConVar* )var)->GetString();

	TelemetrySetLevel( strtoul( pLevel, &pIEnd, 0 ) );
}

static void OnChangeTelemetryFrameCount ( IConVar *var, const char *pOldValue, float flOldValue )
{
	char* pIEnd;
	const char *pFrameCount = (( ConVar* )var)->GetString();

	g_Telemetry.FrameCount = strtoul( pFrameCount, &pIEnd, 0 );
	Msg( " TELEMETRY: Setting Telemetry FrameCount: '%d'\n", g_Telemetry.FrameCount );
}

static void OnChangeTelemetryServer ( IConVar *var, const char *pOldValue, float flOldValue )
{
	const char *pServerAddress = (( ConVar* )var)->GetString();

	Q_strncpy( g_Telemetry.ServerAddress, pServerAddress, ARRAYSIZE( g_Telemetry.ServerAddress ) );
	Msg( " TELEMETRY: Setting Telemetry server: '%s'\n", pServerAddress );
}

static void OnChangeTelemetryDemoStart ( IConVar *var, const char *pOldValue, float flOldValue )
{
	char* pIEnd;
	const char *pVal = (( ConVar* )var)->GetString();

	g_Telemetry.DemoTickStart = strtoul( pVal, &pIEnd, 0 );
	if( g_Telemetry.DemoTickStart > 2000 )
	{
		char cmd[ 256 ]; 

		// If we're far away from the start of the demo file, then jump to ~1000 ticks before.
		Q_snprintf( cmd, sizeof( cmd ), "demo_gototick %d", g_Telemetry.DemoTickStart - 1000 ); 
		Cbuf_AddText( cmd ); 
	}
	Msg( " TELEMETRY: Setting Telemetry DemoTickStart: '%d'\n", g_Telemetry.DemoTickStart );
}

static void OnChangeTelemetryDemoEnd ( IConVar *var, const char *pOldValue, float flOldValue )
{
	char* pIEnd;
	const char *pVal = (( ConVar* )var)->GetString();

	g_Telemetry.DemoTickEnd = strtoul( pVal, &pIEnd, 0 );
	Msg( " TELEMETRY: Setting Telemetry DemoTickEnd: '%d'\n", g_Telemetry.DemoTickEnd );
}

ConVar telemetry_pause( "telemetry_pause", "0", 0, "Pause Telemetry", OnChangeTelemetryPause );
ConVar telemetry_resume( "telemetry_resume", "0", 0, "Resume Telemetry", OnChangeTelemetryResume );
ConVar telemetry_framecount( "telemetry_framecount", "0", 0, "Set Telemetry count of frames to capture", OnChangeTelemetryFrameCount );
ConVar telemetry_level( "telemetry_level", "0", 0, "Set Telemetry profile level: 0 being off.", OnChangeTelemetryLevel );
ConVar telemetry_server( "telemetry_server", "localhost", 0, "Set Telemetry server", OnChangeTelemetryServer );
ConVar telemetry_demostart( "telemetry_demostart", "0", 0, "When playing demo, start telemetry on tick #", OnChangeTelemetryDemoStart );
ConVar telemetry_demoend( "telemetry_demoend", "0", 0, "When playing demo, stop telemetry on tick #", OnChangeTelemetryDemoEnd );
#endif

extern bool gfBackground;

static bool host_checkheap = false;

CCommonHostState host_state;


//-----------------------------------------------------------------------------

enum HostThreadMode
{
	HTM_DISABLED,
	HTM_DEFAULT,
	HTM_FORCED,
};

ConVar host_thread_mode( "host_thread_mode", ( IsX360() ) ? "1" : "0", 0, "Run the host in threaded mode, (0 == off, 1 == if multicore, 2 == force)" );
extern ConVar threadpool_affinity;
void OnChangeThreadAffinity( IConVar *var, const char *pOldValue, float flOldValue )
{
	if ( g_pThreadPool->NumThreads() )
	{
		g_pThreadPool->Distribute( threadpool_affinity.GetBool() );
	}
}

ConVar threadpool_affinity( "threadpool_affinity", "1", 0, "Enable setting affinity", 0, 0, 0, 0, &OnChangeThreadAffinity );

#if 0
extern ConVar threadpool_reserve;
CThreadEvent g_ReleaseThreadReservation( true );
CInterlockedInt g_NumReservedThreads;

void ThreadPoolReserverFunction()
{
	g_ReleaseThreadReservation.Wait();
	--g_NumReservedThreads;
}

void ReserveThreads( int nToReserve )
{
	nToReserve = clamp( nToReserve, 0, g_pThreadPool->NumThreads() );
	g_ReleaseThreadReservation.Set();

	while ( g_NumReservedThreads != 0 )
	{
		ThreadSleep( 0 );
	}

	g_ReleaseThreadReservation.Reset();

	while ( nToReserve-- )
	{
		g_NumReservedThreads++;
		g_pThreadPool->QueueCall( &ThreadPoolReserverFunction )->Release();
	}

	Msg( "%d threads being reserved\n", (int)g_NumReservedThreads );
}

void OnChangeThreadReserve( IConVar *var, const char *pOldValue, float flOldValue )
{
	ReserveThreads( threadpool_reserve.GetInt() );
}

ConVar threadpool_reserve( "threadpool_reserve", "0", 0, "Consume the specified number of threads in the thread pool", 0, 0, 0, 0, &OnChangeThreadReserve );

CON_COMMAND( threadpool_cycle_reserve, "Cycles threadpool reservation by powers of 2" )
{
	int nCores = g_pThreadPool->NumThreads() + 1;
	int nAvailableCores = nCores - g_NumReservedThreads;
	Assert( nAvailableCores );
	int ratio = nCores / nAvailableCores;
	ratio *= 2;
	if ( ratio > nCores )
	{
		ReserveThreads( 0 );
	}
	else
	{
		ReserveThreads( nCores - nCores / ratio );
	}
}

CON_COMMAND( thread_test_tslist, "" )
{
	int nTests = ( args.ArgC() == 1 ) ? 10000 : atoi( args.Arg( 1 ) );
	RunTSListTests( nTests );
}

CON_COMMAND( thread_test_tsqueue, "" )
{
	int nTests = ( args.ArgC() == 1 ) ? 10000 : atoi( args.Arg( 1 ) );
	RunTSQueueTests( nTests );
}

CON_COMMAND( threadpool_run_tests, "" )
{
	int nTests = ( args.ArgC() == 1 ) ? 1 : atoi( args.Arg( 1 ) );
	for ( int i = 0; i < nTests; i++ )
	{
		RunThreadPoolTests();
	}
}
#endif

//-----------------------------------------------------------------------------

/*
A server can always be started, even if the system started out as a client
to a remote system.

A client can NOT be started if the system started as a dedicated server.
Memory is cleared / released when a server or client begins, not when they end.
*/


// Ear position + orientation
static AudioState_t s_AudioState;

engineparms_t host_parms;

bool		host_initialized = false;		// true if into command execution

float		host_frametime = 0.0f;
float		host_frametime_unbounded = 0.0f;
float		host_frametime_stddeviation = 0.0f;
double		realtime = 0;			// without any filtering or bounding
double		host_idealtime = 0;		// "ideal" server time assuming perfect tick rate
float		host_nexttick = 0;		// next server tick in this many ms
float		host_jitterhistory[128] = { 0 };
unsigned int host_jitterhistorypos = 0;

int			host_framecount;
static int	host_hunklevel;

CGameClient	*host_client;			// current client

jmp_buf 	host_abortserver;
jmp_buf     host_enddemo;

static ConVar	host_profile( "host_profile","0" );

ConVar	host_limitlocal( "host_limitlocal", "0", 0, "Apply cl_cmdrate and cl_updaterate to loopback connection" );
ConVar	host_framerate( "host_framerate","0", FCVAR_REPLICATED, "Set to lock per-frame time elapse." );
ConVar	host_timescale( "host_timescale","1.0", FCVAR_REPLICATED, "Prescale the clock by this amount." );
ConVar	host_speeds( "host_speeds","0", 0, "Show general system running times." );		// set for running times

ConVar	host_flush_threshold( "host_flush_threshold", "20", 0, "Memory threshold below which the host should flush caches between server instances" );

void HostTimerSpinMsChangedCallback( IConVar *var, const char *pOldString, float flOldValue );
ConVar host_timer_spin_ms( "host_timer_spin_ms", "0", FCVAR_NONE, "Use CPU busy-loop for improved timer precision (dedicated only)", HostTimerSpinMsChangedCallback );

void HostTimerSpinMsChangedCallback( IConVar *var, const char *pOldString, float flOldValue )
{
	const char *pForcedValue = CommandLine()->ParmValue( "+host_timer_spin_ms" );
	if ( pForcedValue != NULL )
	{
		if ( V_strcmp( host_timer_spin_ms.GetString(), pForcedValue ) )
		{
			Msg( "Value for host_timer_spin_ms is locked to %s by command line parameter.\n", pForcedValue );
			host_timer_spin_ms.SetValue( pForcedValue );
		}
	}
}

CON_COMMAND( host_timer_report, "Spew CPU timer jitter for the last 128 frames in microseconds (dedicated only)" )
{
	if ( sv.IsDedicated() )
	{
		for (int i = 1; i <= ARRAYSIZE( host_jitterhistory ); ++i)
		{
			unsigned int slot = ( i + host_jitterhistorypos ) % ARRAYSIZE( host_jitterhistory );
			Msg( "%1.3fms\n", host_jitterhistory[ slot ] * 1000 );
		}
	}
}

#ifdef REL_TO_STAGING_MERGE_TODO							 
// Do this when merging the game DLLs so FCVAR_CHEAT can be set on them at the same time.
ConVar  developer( "developer", "0", FCVAR_CHEAT, "Set developer message level");
#else
ConVar  developer( "developer", "0", 0, "Set developer message level");
#endif

ConVar	skill( "skill","1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Game skill level (1-3).", true, 1, true, 3 );			// 1 - 3
ConVar	deathmatch( "deathmatch","0", FCVAR_NOTIFY | FCVAR_INTERNAL_USE, "Running a deathmatch server." );	// 0, 1, or 2
ConVar	coop( "coop","0", FCVAR_NOTIFY, "Cooperative play." );			// 0 or 1

#ifdef _DEBUG
ConVar	r_ForceRestore( "r_ForceRestore", "0", 0 );
#endif // _DEBUG

ConVar vcr_verbose( "vcr_verbose", "0", 0, "Write extra information into .vcr file." );

#ifndef SWDS
void CL_CheckToDisplayStartupMenus(); // in cl_main.cpp
#endif


bool GetFileFromRemoteStorage( ISteamRemoteStorage *pRemoteStorage, const char *pszRemoteFileName, const char *pszLocalFileName )
{
	bool bSuccess = false;

	// check if file exists in Steam Cloud first
	int32 nFileSize = pRemoteStorage->GetFileSize( pszRemoteFileName );

	if ( nFileSize > 0 )
	{
		CUtlMemory<char> buf( 0, nFileSize );
		if ( pRemoteStorage->FileRead( pszRemoteFileName, buf.Base(), nFileSize ) == nFileSize )
		{

			char filepath[ 512 ];
			Q_strncpy( filepath, pszLocalFileName, sizeof( filepath ) );
			Q_StripFilename( filepath );
			g_pFullFileSystem->CreateDirHierarchy( filepath, "MOD" );

			FileHandle_t hFile = g_pFileSystem->Open( pszLocalFileName, "wb", "MOD" );
			if( hFile )
			{

				bSuccess = g_pFileSystem->Write( buf.Base(), nFileSize, hFile ) == nFileSize;
				g_pFileSystem->Close( hFile );

				if ( bSuccess )
				{
					DevMsg( "[Cloud]: SUCCEESS retrieved %s from remote storage into %s\n", pszRemoteFileName, pszLocalFileName );
				}
				else
				{
					DevMsg( "[Cloud]: FAILED retrieved %s from remote storage into %s\n", pszRemoteFileName, pszLocalFileName );
				}
			}
		}
	}

	return bSuccess;
}


void CCommonHostState::SetWorldModel( model_t *pModel )
{
	worldmodel = pModel;
	if ( pModel )
	{
		worldbrush = pModel->brush.pShared;
	}
	else
	{
		worldbrush = NULL;
	}
}

void Host_DefaultMapFileName( const char *pFullMapName, /* out */ char *pDiskName, unsigned int nDiskNameSize )
{
	if ( IsPC() || !IsX360() )
	{
		// pc names are as is
		Q_snprintf( pDiskName, nDiskNameSize, "maps/%s.bsp", pFullMapName );
	}
	else if ( IsX360() )
	{
		Q_snprintf( pDiskName, nDiskNameSize, "maps/%s.360.bsp", pFullMapName );
	}
}

void Host_SetAudioState( const AudioState_t &audioState )
{
	memcpy( &s_AudioState, &audioState, sizeof(AudioState_t) );
}

bool Host_IsSinglePlayerGame( void )
{
	if ( sv.IsActive() )
	{
		return !sv.IsMultiplayer();
	}
	else
	{
		return cl.m_nMaxClients == 1;
	}
}

void CheckForFlushMemory( const char *pCurrentMapName, const char *pDestMapName )
{
	if ( host_flush_threshold.GetInt() == 0 )
		return;

#if defined(_X360)
	// There are three cases in which we flush memory
	//   Case 1: changing from one map to another
	//          -> flush temp data caches
	//   Case 2: loading any map (inc. A to A) and free memory is below host_flush_threshold MB
	//          -> flush everything
	//   Case 3: loading a 'blacklisted' map (the known biggest memory users, or where texture sets change)
	//          -> flush everything
	static const char *mapBlackList[] = 
	{
		// --hl2--
		"d1_canals_01",
		"d1_canals_05",
		"d1_eli_01",
		"d1_town_01",
		"d2_coast_01",
		"d2_prison_01",
		"d3_c17_01",
		"d3_c17_05",
		"d3_c17_09",
		"d3_citadel_01",
		"d3_breen_01",
		// --ep1--
		"ep1_c17_02",
		"ep1_c17_02b",
		"ep1_c17_05",
		"ep1_c17_06",
		// --ep2--
		"ep2_outland_06a",
		"ep2_outland_09",
		"ep2_outland_11",
		"ep2_outland_12",
		"ep2_outland_12a",
		// --tf--
		"tc_hydro"
	};

	char szCurrentMapName[MAX_PATH];
	char szDestMapName[MAX_PATH];
	if ( pCurrentMapName )
	{
		V_FileBase( pCurrentMapName, szCurrentMapName, sizeof( szCurrentMapName ) );
	}
	else
	{
		szCurrentMapName[0] = '\0';
	}
	pCurrentMapName = szCurrentMapName;

	if ( pDestMapName )
	{
		V_FileBase( pDestMapName, szDestMapName, sizeof( szDestMapName ) );
	}
	else
	{
		szDestMapName[0] = '\0';
	}
	pDestMapName = szDestMapName;

	bool bIsMapChanging = pCurrentMapName[0] && V_stricmp( pCurrentMapName, pDestMapName );

	bool bIsDestMapBlacklisted = false;
	for ( int i = 0; i < ARRAYSIZE( mapBlackList ); i++ )
	{
		if ( pDestMapName && !V_stricmp( pDestMapName, mapBlackList[i] ) )
		{
			bIsDestMapBlacklisted = true;
		}
	}

	DevMsg( "---CURRENT(%s), NEXT(%s)\n", (pCurrentMapName[0] ? pCurrentMapName : "----"), (pDestMapName[0] ? pDestMapName : "----") );
	if ( bIsMapChanging )
	{
		DevMsg( "---CHANGING MAPS!\n" );
	}
	if ( bIsDestMapBlacklisted )
	{
		DevMsg( "---BLACKLISTED!\n" );
	}

	MEMORYSTATUS stat;
	GlobalMemoryStatus( &stat );
	if ( ( stat.dwAvailPhys < host_flush_threshold.GetInt() * 1024 * 1024 ) ||
		 ( bIsDestMapBlacklisted && bIsMapChanging ) )
	{
		// Flush everything; ALL data is reloaded from scratch
		SV_FlushMemoryOnNextServer();
		g_pDataCache->Flush();
		DevWarning( "---FULL FLUSH\n" );
	}
	else if ( bIsMapChanging )
	{
		// Flush temporary data (async anim, non-locked async audio)
		g_pMDLCache->Flush( MDLCACHE_FLUSH_ANIMBLOCK );
		wavedatacache->Flush();
		DevWarning( "---PARTIAL FLUSH\n" );
	}
	DevMsg( "---- --- ----\n" );
#endif
}

void Host_AbortServer()
{
	g_HostServerAbortCount++;
	longjmp( host_abortserver, 1 );
}

/*
================
Host_EndGame
================
*/
void Host_EndGame (bool bShowMainMenu, const char *message, ...)
{
	int oldn;
	va_list		argptr;
	char		string[1024];

	va_start (argptr,message);
	Q_vsnprintf (string,sizeof(string),message,argptr);
	va_end (argptr);
	ConMsg ("Host_EndGame: %s\n",string);

#ifndef SWDS
	scr_disabled_for_loading = true;
#endif

	oldn = cl.demonum;
	cl.demonum = -1;

	Host_Disconnect(bShowMainMenu);

	cl.demonum = oldn;

	if ( sv.IsDedicated() )
	{
		Sys_Error ("Host_EndGame: %s\n",string);	// dedicated servers exit
		return;
	}

	if (cl.demonum != -1)
	{
#ifndef SWDS
		CL_NextDemo ();
#endif		
		g_HostEndDemo++;
		longjmp (host_enddemo, 1);
	}
	else
	{

#ifndef SWDS
		scr_disabled_for_loading = false;
#endif
		if ( g_bAbortServerSet )
		{
			Host_AbortServer();
		}
	}
}

/*
================
Host_Error

This shuts down both the client and server
================
*/
void Host_Error (const char *error, ...)
{
	va_list		argptr;
	char		string[1024];
	static	bool inerror = false;

	DebuggerBreakIfDebugging_StagingOnly();

	g_HostErrorCount++;

	if (inerror)
	{
		Sys_Error ("Host_Error: recursively entered");
	}
	inerror = true;

#ifndef SWDS
	//	CL_WriteMessageHistory();	TODO must be done by network layer
#endif

	va_start (argptr,error);
	Q_vsnprintf(string,sizeof(string),error,argptr);
	va_end (argptr);

	if ( sv.IsDedicated() )
	{
		// dedicated servers just exit
		Sys_Error( "Host_Error: %s\n", string );
		return;
	}

#ifndef SWDS
	// Reenable screen updates
	SCR_EndLoadingPlaque ();		
#endif
	ConMsg( "\nHost_Error: %s\n\n", string );

	Host_Disconnect( true, string );

	cl.demonum = -1;

	inerror = false;

	if ( g_bAbortServerSet )
	{
		Host_AbortServer();
	}
}

#ifndef SWDS

ButtonCode_t nInvalidKeyBindings[] =
{
	KEY_PAD_0,
	KEY_PAD_1,
	KEY_PAD_2,
	KEY_PAD_3,
	KEY_PAD_4,
	KEY_PAD_5,
	KEY_PAD_6,
	KEY_PAD_7,
	KEY_PAD_8,
	KEY_PAD_9,
	KEY_PAD_DIVIDE,
	KEY_PAD_MULTIPLY,
	KEY_PAD_MINUS,
	KEY_PAD_PLUS,
	KEY_PAD_ENTER,
	KEY_PAD_DECIMAL,
	KEY_ESCAPE,
	KEY_SCROLLLOCK,
	KEY_INSERT,
	KEY_DELETE,
	KEY_HOME,
	KEY_END,
	KEY_PAGEUP,
	KEY_PAGEDOWN,
	KEY_BREAK,
	KEY_LSHIFT,
	KEY_RSHIFT,
	KEY_LALT,
	KEY_RALT,
	KEY_LCONTROL,
	KEY_RCONTROL,
	KEY_LWIN,
	KEY_RWIN,
	KEY_APP,
	KEY_UP,
	KEY_LEFT,
	KEY_DOWN,
	KEY_RIGHT,
	KEY_CAPSLOCKTOGGLE,
	KEY_NUMLOCKTOGGLE,
	KEY_SCROLLLOCKTOGGLE,
	KEY_BACKQUOTE,
};

//******************************************
// SetupNewBindings
//
// In the rare case that we ship an update
// where we need a key to be bound to a given
// con-command, this function do its best to
// bind those keys, based on a file called
// newbindings.txt.
//******************************************
void SetupNewBindings()
{
	char szBindCmd[ 256 ];

	// Load the file
	const char *pFilename = "scripts\\newbindings.txt";
	KeyValues *pNewBindingsData = new KeyValues( pFilename );
	if ( !pNewBindingsData->LoadFromFile( g_pFileSystem, pFilename ) )
	{
		pNewBindingsData->deleteThis();
		return;
	}

	FOR_EACH_TRUE_SUBKEY( pNewBindingsData, pSubKey )
	{
		// Get the binding
		const char *pBinding = pSubKey->GetName();
		if ( !pBinding )
			continue;

		// Get the ideal key
		const char *pIdealKey = pSubKey->GetString( "ideal_key", NULL );
		if ( !pIdealKey )
			continue;

		// Force the key binding if the ideal key is bound to an irrelevant command, which is the
		// case for CS:S, which binds F6 to "quick save," which has no used in the game at all.
		const char *pOverrideIfCmd = pSubKey->GetString( "override_if", NULL );
		if ( pOverrideIfCmd )
		{
			const char *pCurrentBindingForKey = ::Key_BindingForKey( g_pInputSystem->StringToButtonCode( pIdealKey ) );
			if ( !pCurrentBindingForKey  || !V_stricmp( pOverrideIfCmd, pCurrentBindingForKey ) )
			{
				V_snprintf( szBindCmd, sizeof( szBindCmd ), "bind \"%s\" \"%s\"", pIdealKey, pBinding );
				Cbuf_AddText( szBindCmd );
				continue;
			}
		}

		// Already have a key for this command?
		if ( ::Key_NameForBinding( pBinding ) )
			continue;

		// No binding.  Is the ideal key available?
		ButtonCode_t bcIdealKey = g_pInputSystem->StringToButtonCode( pIdealKey );
		const char *pCurrentBindingForIdealKey = ::Key_BindingForKey( bcIdealKey );
		if ( !pCurrentBindingForIdealKey )
		{
			// Yes - bind to the ideal key.
			V_snprintf( szBindCmd, sizeof( szBindCmd ), "bind \"%s\" \"%s\"", pIdealKey, pBinding );
			Cbuf_AddText( szBindCmd );
			continue;
		}

		// Ideal key already bound - find another key at random and bind it
		bool bFound = false;
		int nNumAttempts = 1;
		for ( int nCurButton = (int)KEY_0; nCurButton <= (int)KEY_LAST; ++nCurButton, ++nNumAttempts )
		{
			// Don't consider numpad, windows keys, etc
			bool bFoundInvalidKey = false;
			for ( int iKeyIndex = 0; iKeyIndex < sizeof( nInvalidKeyBindings )/sizeof( nInvalidKeyBindings[0] ); iKeyIndex++ )
			{
				if ( nCurButton == (int)nInvalidKeyBindings[iKeyIndex] )
				{
					bFoundInvalidKey = true;
					break;
				}
			}

			if ( bFoundInvalidKey )
				continue;

			// Key available?
			ButtonCode_t bcCurButton = (ButtonCode_t)nCurButton;
			if ( !::Key_BindingForKey( bcCurButton ) )
			{
				// Yes - use it.
				V_snprintf( szBindCmd, sizeof( szBindCmd ), "bind \"%s\" \"%s\"", g_pInputSystem->ButtonCodeToString( bcCurButton ), pBinding );
				Cbuf_AddText( szBindCmd );
				bFound = true;
				break;
			}
		}

		// We tried really hard - did we fail?
		if ( !bFound )
		{
			Warning( "Unable to bind a key for command \"%s\" after %i attempt(s).\n", pBinding, nNumAttempts );
		}
	}
}

//******************************************
// UseDefuaultBinding
//
// If the config.cfg file is not present, this
// function is called to set the default key
// bindings to match those defined in kb_def.lst
//******************************************
void UseDefaultBindings( void )
{
	FileHandle_t f;
	char szFileName[ _MAX_PATH ];
	char token[ 1024 ];
	char szKeyName[ 256 ];

	// read kb_def file to get default key binds
	Q_snprintf( szFileName, sizeof( szFileName ), "%skb_def.lst", SCRIPT_DIR );
	f = g_pFileSystem->Open( szFileName, "r");
	if ( !f )
	{
		ConMsg( "Couldn't open kb_def.lst\n" );
		return;
	}

	// read file into memory
	int size = g_pFileSystem->Size(f);
	char *startbuf = new char[ size ];
	g_pFileSystem->Read( startbuf, size, f );
	g_pFileSystem->Close( f );

	const char *buf = startbuf;
	while ( 1 )
	{
		buf = COM_ParseFile( buf, token, sizeof( token ) );
		if ( strlen( token ) <= 0 )
			break;
		Q_strncpy ( szKeyName, token, sizeof( szKeyName ) );

		buf = COM_ParseFile( buf, token, sizeof( token ) );
		if ( strlen( token ) <= 0 )  // Error
			break;

		// finally, bind key
		Key_SetBinding ( g_pInputSystem->StringToButtonCode( szKeyName ), token );
	}
	delete [] startbuf;		// cleanup on the way out
}

static bool g_bConfigCfgExecuted = false;

//-----------------------------------------------------------------------------
// Purpose: Write out our 360 exclusive settings to internal storage
//-----------------------------------------------------------------------------
void Host_WriteConfiguration_360( void )
{
#ifdef _X360
	if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
		return;

	// Construct the name for our config settings for this mod
	char strFilename[MAX_PATH];
	Q_snprintf( strFilename, sizeof(strFilename), "cfg:/%s_config.cfg", GetCurrentMod() );

	// Always throw away all keys that are left over.
	CUtlBuffer	configBuff( 0, 0, CUtlBuffer::TEXT_BUFFER);
	configBuff.Printf( "unbindall\n" );

	Key_WriteBindings( configBuff );
	cv->WriteVariables( configBuff );

	ConVarRef mat_monitorgamma( "mat_monitorgamma" );
	ConVarRef mat_monitorgamma_tv_enabled( "mat_monitorgamma_tv_enabled" );

	char strVideoFilename[MAX_PATH];
	CUtlBuffer videoBuff( 0, 0, CUtlBuffer::TEXT_BUFFER);
	Q_snprintf( strVideoFilename, sizeof(strVideoFilename), "cfg:/video_config.cfg" );
	videoBuff.Printf( "mat_monitorgamma %f\n", mat_monitorgamma.GetFloat() );
	videoBuff.Printf( "mat_monitorgamma_tv_enabled %d\n", mat_monitorgamma_tv_enabled.GetBool() );

	// Anything to write?
	if ( configBuff.TellMaxPut() )
	{
		g_pFileSystem->WriteFile( strFilename, NULL, configBuff );
	}

	if ( videoBuff.TellMaxPut() )
	{
		g_pFileSystem->WriteFile( strVideoFilename, NULL, videoBuff );
	}

	g_pXboxSystem->FinishContainerWrites();
#endif // #ifdef _X360
}

/*
===============
Host_WriteConfiguration

Writes key bindings and archived cvars to config.cfg
===============
*/

void Host_WriteConfiguration( const char *filename, bool bAllVars )
{
	const bool cbIsUserRequested = ( filename != NULL );

	if ( !filename )
		filename = "config.cfg";

	if ( !g_bConfigCfgExecuted )
		return;
	
	if ( !host_initialized )
		return;

	// Don't write config when in default--most of the values are defaults which is not what the player wants.
	// If bAllVars is set, go ahead and write out the file anyways, since it was requested explicitly.
	if ( !cbIsUserRequested && ( CommandLine()->CheckParm( "-default" ) || host_competitive_ever_enabled.GetBool() ) )
		return;
	
	// Write to internal storage on the 360
	if ( IsX360() )
	{
		Host_WriteConfiguration_360();
		return;
	}

	// If in map editing mode don't save configuration
	if (g_bInEditMode)
	{
		ConMsg( "skipping %s output when in map edit mode\n", filename );
		return;
	}

	// dedicated servers initialize the host but don't parse and set the
	// config.cfg cvars
	if ( sv.IsDedicated() )
		return;

	if ( IsPC() && Key_CountBindings() <= 1 )
	{
		ConMsg( "skipping %s output, no keys bound\n", filename );
		return;
	}

	// force any queued convar changes to flush before reading/writing them
	UpdateMaterialSystemConfig();

	// Generate a new .cfg file.
	char		szFileName[MAX_PATH];
	CUtlBuffer	configBuff( 0, 0, CUtlBuffer::TEXT_BUFFER);

	Q_snprintf( szFileName, sizeof(szFileName), "cfg/%s", filename );
	g_pFileSystem->CreateDirHierarchy( "cfg", "MOD" );
	if ( g_pFileSystem->FileExists( szFileName, "MOD" ) && !g_pFileSystem->IsFileWritable( szFileName, "MOD" ) )
	{
		ConMsg( "Config file %s is read-only!!\n", szFileName );
		return;
	}
	
	// Always throw away all keys that are left over.
	configBuff.Printf( "unbindall\n" );

	Key_WriteBindings( configBuff );
	cv->WriteVariables( configBuff, bAllVars );

#if !defined( SWDS )
		bool down;
		if ( g_ClientDLL->IN_IsKeyDown( "in_jlook", down ) && down )
		{
			configBuff.Printf( "+jlook\n" );
		}
#endif // SWDS

	if ( !configBuff.TellMaxPut() )
	{
		// nothing to write
		return;
	}

#if defined(NO_STEAM)
		AssertMsg( false, "SteamCloud not available on Xbox 360. Badger Martin to fix this." );
#else
		ISteamRemoteStorage *pRemoteStorage = SteamClient()?(ISteamRemoteStorage *)SteamClient()->GetISteamGenericInterface(
			SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL;

		if ( pRemoteStorage )
		{
			int32 availableBytes, totalBytes = 0;
			if ( pRemoteStorage->GetQuota( &totalBytes, &availableBytes ) )
			{
				if ( totalBytes > 0 )
				{
					if ( cl_cloud_settings.GetInt() == STEAMREMOTESTORAGE_CLOUD_ON )
					{
						// TODO put MOD dir in pathname
						if ( pRemoteStorage->FileWrite( szFileName, configBuff.Base(), configBuff.TellMaxPut() ) )
						{
							DevMsg( "[Cloud]: SUCCEESS saving %s in remote storage\n", szFileName );
						}
						else
						{
							// probably a quota issue. TODO what to do ?
							DevMsg( "[Cloud]: FAILED saving %s in remote storage\n", szFileName );
						}

						// write the current logo file
						char szLogoFileName[MAX_PATH]; 
						Q_strncpy( szLogoFileName, cl_logofile.GetString(), sizeof(szLogoFileName) ); // .vtf file

						if ( g_pFileSystem->FileExists( szLogoFileName, "MOD" ) )
						{
							// store logo .VTF file
							FileHandle_t hFile = g_pFileSystem->Open( szLogoFileName, "rb", "MOD" );
							if ( FILESYSTEM_INVALID_HANDLE != hFile )
							{
								unsigned int unSize = g_pFileSystem->Size( hFile );

								byte *pBuffer = (byte*) malloc( unSize );
								if ( g_pFileSystem->Read( pBuffer, unSize, hFile ) == unSize )
								{
									Q_SetExtension( g_szDefaultLogoFileName, ".vtf", sizeof(g_szDefaultLogoFileName) );
									if ( pRemoteStorage->FileWrite( g_szDefaultLogoFileName, pBuffer, unSize ) )
									{
										DevMsg( "[Cloud]: SUCCEESS saving %s in remote storage\n", g_szDefaultLogoFileName );
									}
									else
									{
										DevMsg( "[Cloud]: FAILED saving %s in remote storage\n", g_szDefaultLogoFileName );
									}
								}
								free( pBuffer );
								g_pFileSystem->Close( hFile );
							}

							// store logo .VMT file
							Q_SetExtension( szLogoFileName, ".vmt", sizeof(szLogoFileName) );
							hFile = g_pFileSystem->Open( szLogoFileName, "rb", "MOD" );
							if ( FILESYSTEM_INVALID_HANDLE != hFile )
							{
								unsigned int unSize = g_pFileSystem->Size( hFile );

								byte *pBuffer = (byte*) malloc( unSize );
								if ( g_pFileSystem->Read( pBuffer, unSize, hFile ) == unSize )
								{
									Q_SetExtension( g_szDefaultLogoFileName, ".vmt", sizeof(g_szDefaultLogoFileName) );
									if ( pRemoteStorage->FileWrite( g_szDefaultLogoFileName, pBuffer, unSize ) )
									{
										DevMsg( "[Cloud]: SUCCEESS saving %s in remote storage\n", g_szDefaultLogoFileName );
									}
									else
									{
										DevMsg( "[Cloud]: FAILED saving %s in remote storage\n", g_szDefaultLogoFileName );
									}
								}
								free( pBuffer );
								g_pFileSystem->Close( hFile );
							}
						}
					}
				}
			}

			// even if SteamCloud worked we still safe the same file locally
		}
#endif


	// make a persistent copy that async will use and free
	char *tempBlock = new char[configBuff.TellMaxPut()];
	Q_memcpy( tempBlock, configBuff.Base(), configBuff.TellMaxPut() );

	// async write the buffer, and then free it
	g_pFileSystem->AsyncWrite( szFileName, tempBlock, configBuff.TellMaxPut(), true );

	ConMsg( "Host_WriteConfiguration: Wrote %s\n", szFileName );
}

//-----------------------------------------------------------------------------
// Purpose: Retrieve and set any defaults from the user's gamer profile
//-----------------------------------------------------------------------------
bool XBX_SetProfileDefaultSettings( void )
{
	// These defined values can't play nicely with the PC, so we need to ignore them for that build target
#ifdef _X360
	// These will act as indices into the array that is returned by the API
	enum
	{
		XPS_GAMER_DIFFICULTY,
		XPS_GAMER_ACTION_MOVEMENT_CONTROL,
		XPS_GAMER_YAXIS_INVERSION,
		XPS_OPTION_CONTROLLER_VIBRATION,
		NUM_PROFILE_SETTINGS
	};

	// These are the values we're interested in having returned (must match the indices above)
	const DWORD dwSettingIds[ NUM_PROFILE_SETTINGS ] =
	{
		XPROFILE_GAMER_DIFFICULTY,
		XPROFILE_GAMER_ACTION_MOVEMENT_CONTROL,
		XPROFILE_GAMER_YAXIS_INVERSION,
		XPROFILE_OPTION_CONTROLLER_VIBRATION
	};

	// Must have a valid primary user by this point
	int nPrimaryID = XBX_GetPrimaryUserId();

	// First, we call with a NULL pointer and zero size to retrieve the buffer size we'll get back
	DWORD dwResultSize = 0;	// Must be zero to get the correct size back
	XUSER_READ_PROFILE_SETTING_RESULT *pResults = NULL;
	DWORD dwError = XUserReadProfileSettings(	0,			// Family ID (current title)
												nPrimaryID,	// User ID
												NUM_PROFILE_SETTINGS,
												dwSettingIds,
												&dwResultSize,
												pResults,
												NULL );

	// We need this to inform us that it's given us a size back for the buffer
	if ( dwError != ERROR_INSUFFICIENT_BUFFER )
		return false;

	// Now we allocate that buffer and supply it to the call
	BYTE *pData = (BYTE *) stackalloc( dwResultSize );
	ZeroMemory( pData, dwResultSize );

	pResults = (XUSER_READ_PROFILE_SETTING_RESULT *) pData;

	dwError = XUserReadProfileSettings(	0,			// Family ID (current title)
										nPrimaryID,	// User ID
										NUM_PROFILE_SETTINGS,
										dwSettingIds,
										&dwResultSize,
										pResults,
										NULL );	// Not overlapped, must be synchronous

	// We now have a raw buffer of results
	if ( dwError != ERROR_SUCCESS )
		return false;

	//
	// Skill
	//

	XUSER_PROFILE_SETTING *pSetting = pResults->pSettings + XPS_GAMER_DIFFICULTY;
	Assert( pSetting->data.type == XUSER_DATA_TYPE_INT32 );
	
	int nSkillSetting = pSetting->data.nData;
	int nResultSkill = 0;
	switch( nSkillSetting )
	{
	case XPROFILE_GAMER_DIFFICULTY_NORMAL:
		nResultSkill = 2;
		break;
	
	case XPROFILE_GAMER_DIFFICULTY_HARD:
		nResultSkill = 3;
		break;
	
	case XPROFILE_GAMER_DIFFICULTY_EASY:
	default:
		nResultSkill = 1;
		break;
	}

	// If the mod has no difficulty setting, only easy is allowed
	KeyValues *modinfo = new KeyValues("ModInfo");
	if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) )
	{
		if ( stricmp(modinfo->GetString("nodifficulty", "0"), "1") == 0 )
			nResultSkill = 1;
	}
	modinfo->deleteThis();

	char szScratch[MAX_PATH];
	Q_snprintf( szScratch, sizeof(szScratch), "skill %d", nResultSkill );
	Cbuf_AddText( szScratch );

	// 
	// Movement control
	//

	pSetting = pResults->pSettings + XPS_GAMER_ACTION_MOVEMENT_CONTROL;
	Assert( pSetting->data.type == XUSER_DATA_TYPE_INT32 );
	Q_snprintf( szScratch, sizeof(szScratch), "joy_movement_stick %d", ( pSetting->data.nData == XPROFILE_ACTION_MOVEMENT_CONTROL_L_THUMBSTICK ) ? 0 : 1 );
	Cbuf_AddText( szScratch );
	Q_snprintf( szScratch, sizeof(szScratch), "joy_movement_stick_default %d", ( pSetting->data.nData == XPROFILE_ACTION_MOVEMENT_CONTROL_L_THUMBSTICK ) ? 0 : 1 );
	Cbuf_AddText( szScratch );
	Cbuf_AddText( "joyadvancedupdate" );

	// 
	// Y-Inversion
	//

	pSetting = pResults->pSettings + XPS_GAMER_YAXIS_INVERSION;
	Assert( pSetting->data.type == XUSER_DATA_TYPE_INT32 );
	Q_snprintf( szScratch, sizeof(szScratch), "joy_inverty %d", pSetting->data.nData );
	Cbuf_AddText( szScratch );
	Q_snprintf( szScratch, sizeof(szScratch), "joy_inverty_default %d", pSetting->data.nData );
	Cbuf_AddText( szScratch );
	
	//
	// Vibration control
	//

	pSetting = pResults->pSettings + XPS_OPTION_CONTROLLER_VIBRATION;
	Assert( pSetting->data.type == XUSER_DATA_TYPE_INT32 );
	Q_snprintf( szScratch, sizeof(szScratch), "cl_rumblescale %d", ( pSetting->data.nData != 0 ) ? 1 : 0 );
	Cbuf_AddText( szScratch );

	// Execute all commands we've queued up
	Cbuf_Execute();
#endif // _X360

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Read our configuration from the 360, filling in defaults on our first run
//-----------------------------------------------------------------------------
void Host_ReadConfiguration_360( void )
{
#ifdef _X360
	
	// Exec our defaults on the first pass
	if ( g_bConfigCfgExecuted == false )
	{
		// First, we exec our default configuration for the 360
		Cbuf_AddText( "exec config.360.cfg game\n" );
		Cbuf_Execute();
	}

	// Can't do any more in this function if we don't have a valid user id
	if ( XBX_GetPrimaryUserId() == INVALID_USER_ID )
		return;

	// Build the config name we're looking for
	char strFileName[MAX_PATH];
	Q_snprintf( strFileName, sizeof(strFileName), "cfg:/%s_config.cfg", GetCurrentMod() );

	bool bStorageDeviceValid = ( XBX_GetStorageDeviceId() != XBX_INVALID_STORAGE_ID && XBX_GetStorageDeviceId() != XBX_STORAGE_DECLINED );
	bool bSaveConfig = false;

	// Call through normal API function once the content container is opened
	if ( CommandLine()->CheckParm( "-forcexboxreconfig" ) || bStorageDeviceValid == false || g_pFileSystem->FileExists( strFileName ) == false )
	{
		// If we've already done this in this session, never do it again (we don't want to stomp their settings under any circumstances)
		if ( g_bConfigCfgExecuted == false )
		{
			// Get and set all our default setting we care about from the Xbox
			XBX_SetProfileDefaultSettings();
		}
		
		// Save out what we have
		bSaveConfig = true;
	}
	else
	{
		// Otherwise, exec the user settings stored on the 360
		char szCommand[MAX_PATH];
		Q_snprintf( szCommand, sizeof( szCommand ), "exec %s_config.cfg x360\n", GetCurrentMod() );
		Cbuf_AddText( szCommand );
		
		// Exec the video config as well
		Q_snprintf( szCommand, sizeof( szCommand ), "exec video_config.cfg x360\n", GetCurrentMod() );
		Cbuf_AddText( szCommand );
		Cbuf_Execute();
	}

	// Mark that we've loaded a config and can now save it
	g_bConfigCfgExecuted = true;

	if ( bSaveConfig )
	{
		// An ugly hack, but we can probably save this safely
		bool saveinit = host_initialized;
		host_initialized = true;
		Host_WriteConfiguration_360();
		host_initialized = saveinit;
	}
#endif // _X360
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : false - 
//-----------------------------------------------------------------------------
void Host_ReadConfiguration()
{
	if ( sv.IsDedicated() )
		return;

	// Rebind keys and set cvars
	if ( !g_pFileSystem )
	{
		Sys_Error( "Host_ReadConfiguration:  g_pFileSystem == NULL\n" );
	}

	// Handle the 360 case
	if ( IsX360() )
	{
		Host_ReadConfiguration_360();
		return;
	}

	bool saveconfig = false;

	ISteamRemoteStorage *pRemoteStorage = SteamClient()?(ISteamRemoteStorage *)SteamClient()->GetISteamGenericInterface(
		SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), STEAMREMOTESTORAGE_INTERFACE_VERSION ):NULL;
	
	if ( pRemoteStorage )
	{
		// if cloud settings is default but remote storage does not exist yet, set it to sync all because this is the first
		// computer the game is run on--default to copying everything to the cloud
		if ( !pRemoteStorage->FileExists( "cfg/config.cfg" ) )
		{
			DevMsg( "[Cloud]: Default setting with remote data non-existent, sync all\n" );
			cl_cloud_settings.SetValue( STEAMREMOTESTORAGE_CLOUD_ON );
		}

		if ( cl_cloud_settings.GetInt() == STEAMREMOTESTORAGE_CLOUD_ON )
		{
			// config files are run through the exec command which got pretty complicated with all the splitscreen
			// stuff. Steam UFS doens't support split screen well (2 users ?)
			GetFileFromRemoteStorage( pRemoteStorage, "cfg/config.cfg", "cfg/config.cfg" );
		}
	}

	if ( g_pFileSystem->FileExists( "//mod/cfg/config.cfg" ) )
	{
		Cbuf_AddText( "exec config.cfg\n" );
	}
	else
	{
		Cbuf_AddText( "exec config_default.cfg\n" );
		saveconfig = true;
	}

	Cbuf_Execute();

	if ( pRemoteStorage )
	{
		if ( cl_cloud_settings.GetInt() == STEAMREMOTESTORAGE_CLOUD_ON )
		{
			// get logo .VTF file
			Q_SetExtension( g_szDefaultLogoFileName, ".vtf", sizeof(g_szDefaultLogoFileName) );
			GetFileFromRemoteStorage( pRemoteStorage, g_szDefaultLogoFileName, g_szDefaultLogoFileName );

			cl_logofile.SetValue( g_szDefaultLogoFileName );

			// get logo .VMT file
			Q_SetExtension( g_szDefaultLogoFileName, ".vmt", sizeof(g_szDefaultLogoFileName) );
			GetFileFromRemoteStorage( pRemoteStorage, g_szDefaultLogoFileName, g_szDefaultLogoFileName );
		}
	}

	// check to see if we actually set any keys, if not, load defaults from kb_def.lst
	// so we at least have basics setup.
	int nNumBinds = Key_CountBindings();
	if ( nNumBinds == 0 )
	{
		UseDefaultBindings();
	}
	else
	{
		// Setup new bindings - if we ship an update that requires that every user has a key bound for
		// X concommand, SetupNewBindings() will do its best to ensure that a key is bound.  We assume
		// that kb_def.lst includes any bindings that SetupNewBindings() might include in the case
		// that nNumBinds == 0 above.
		SetupNewBindings();
	}

	Key_SetBinding( KEY_ESCAPE, "cancelselect" );

	// Make sure that something is always bound to console
	if (NULL == Key_NameForBinding("toggleconsole"))
	{
		// If nothing is bound to it then bind it to '
		Key_SetBinding( KEY_BACKQUOTE, "toggleconsole" );
	}

	SetupDefaultAskConnectAcceptKey();

	g_bConfigCfgExecuted = true;

	if ( saveconfig )
	{
		// An ugly hack, but we can probably save this safely
		bool saveinit = host_initialized;
		host_initialized = true;
		Host_WriteConfiguration();
		host_initialized = saveinit;
	}
}

CON_COMMAND( host_writeconfig, "Store current settings to config.cfg (or specified .cfg file)." )
{
	if ( args.ArgC() > 3 )
	{
		ConMsg( "Usage:  writeconfig <filename.cfg> [full]\n" );
		ConMsg( "<filename.cfg> is required, optionally specify \"full\" to write out all archived convars.\n" );
		ConMsg( "By default, only non-default values are written out.\n" );
		return;
	}

	if ( args.ArgC() >= 2 )
	{
		bool bWriteAll = ( args.ArgC() == 3 && V_stricmp( args[ 2 ], "full" ) == 0 );

		char const *filename = args[ 1 ];
		if ( !filename || !filename[ 0 ] )
		{
			return;
		}

		char outfile[ MAX_QPATH ];
		// Strip path and extension from filename
		Q_FileBase( filename, outfile, sizeof( outfile ) );
		Host_WriteConfiguration( va( "%s.cfg", outfile ), bWriteAll );
		if  ( !bWriteAll )
			ConMsg( "Wrote partial config file \"%s\" out, to write full file use host_writeconfig \"%s\" full\n", outfile, outfile );
	}
	else
	{
		Host_WriteConfiguration( NULL, true );
	}
}

#endif

//-----------------------------------------------------------------------------
// Purpose: Does a quick parse of the config.cfg to read cvars that
//			need to be read before any games systems are initialized
//			assumes only cvars and filesystem are initialized
//-----------------------------------------------------------------------------
void Host_ReadPreStartupConfiguration()
{
	FileHandle_t f = NULL;
	if ( IsX360() )
	{
		// 360 config is less restrictive and can be anywhere in the game path
		f = g_pFileSystem->Open( "//game/cfg/config.360.cfg", "rt" );
	}
	else
	{
		f = g_pFileSystem->Open( "//mod/cfg/config.cfg", "rt" );
	}

	if ( !f )
		return;

	// read file into memory
	int size = g_pFileSystem->Size(f);
	char *configBuffer = new char[ size + 1 ];
	g_pFileSystem->Read( configBuffer, size, f );
	configBuffer[size] = 0;
	g_pFileSystem->Close( f );

	// parse out file
	static const char *s_PreStartupConfigConVars[] =
	{
		"sv_unlockedchapters",		// needed to display the startup graphic while loading
		"snd_legacy_surround",		// needed to init the sound system
		"gameui_xbox",				// needed to initialize the correct UI
		"save_in_memory"			// needed to preread data from the correct location in UI
	};

	// loop through looking for all the cvars to apply
	for (int i = 0; i < ARRAYSIZE(s_PreStartupConfigConVars); i++)
	{
		const char *search = Q_stristr(configBuffer, s_PreStartupConfigConVars[i]);
		if (search)
		{
			// read over the token
			search = COM_Parse(search);

			// read the value
			COM_Parse(search);

			// apply the value
			ConVar *var = (ConVar *)g_pCVar->FindVar( s_PreStartupConfigConVars[i] );
			if ( var )
			{
				var->SetValue( com_token );
			}
		}
	}

	// free
	delete [] configBuffer;
}

void Host_RecomputeSpeed_f( void )
{
	ConMsg( "Recomputing clock speed...\n" );

	CClockSpeedInit::Init();
	ConMsg( "Clock speed: %.0f Mhz\n", CFastTimer::GetClockSpeed() / 1000000.0 );
}

static ConCommand recompute_speed( "recompute_speed", Host_RecomputeSpeed_f, "Recomputes clock speed (for debugging purposes).", FCVAR_CHEAT );

void DTI_Flush_f()
{
	DTI_Flush();
	ServerDTI_Flush();
}

static ConCommand dti_flush( "dti_flush", DTI_Flush_f, "Write out the datatable instrumentation files (you must run with -dti for this to work)." );

/*
==================
Host_ShutdownServer

This only happens at the end of a game, not between levels
==================
*/
void Host_ShutdownServer( void )
{
	if ( !sv.IsActive() )
		return;

	master->ShutdownConnection();

	Host_AllowQueuedMaterialSystem( false );
	// clear structures
#if !defined( SWDS )
	g_pShadowMgr->LevelShutdown();
#endif
	StaticPropMgr()->LevelShutdown();

	Host_FreeStateAndWorld( true );
	sv.Shutdown();// sv.Shutdown() references some heap memory, so run it before Host_FreeToLowMark()
	Host_FreeToLowMark( true ); 

	IGameEvent *event = g_GameEventManager.CreateEvent( "server_shutdown" );

	if ( event )
	{
		event->SetString( "reason", "restart" );
		g_GameEventManager.FireEvent( event );
	}

	g_Log.Close();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : time - 
// Output : bool
//-----------------------------------------------------------------------------
void Host_AccumulateTime( float dt )
{
	// Accumulate some time
	realtime += dt;

	bool bUseNormalTickTime = true;
#if !defined(SWDS)
	if ( demoplayer->IsPlayingTimeDemo() )
		bUseNormalTickTime = false;
#endif
	if ( g_bDedicatedServerBenchmarkMode )
		bUseNormalTickTime = false;

	if ( bUseNormalTickTime )
	{
		host_frametime	= dt;
	}
	else
	{
		// Used to help increase reproducibility of timedemos
		host_frametime	= host_state.interval_per_tick;
	}

#if 1
	if ( host_framerate.GetFloat() > 0 
#if !defined(SWDS)
		&& ( CanCheat() || demoplayer->IsPlayingBack() ) 
#endif
		)
	{
		float fps = host_framerate.GetFloat();
		if ( fps > 1 )
		{
			fps = 1.0f/fps;
		}
		host_frametime = fps;

#if !defined(SWDS) && defined( REPLAY_ENABLED )
		extern IDemoPlayer *g_pReplayDemoPlayer;
		if ( demoplayer->IsPlayingBack() && demoplayer == g_pReplayDemoPlayer )
		{
			// adjust time scale if playing back demo
			host_frametime *= demoplayer->GetPlaybackTimeScale();
		}
#endif

		host_frametime_unbounded = host_frametime;
	}
	else if (host_timescale.GetFloat() > 0 
#if !defined(SWDS)
		&& ( CanCheat() || demoplayer->IsPlayingBack() ) 
#endif
		)
	{
		float fullscale = host_timescale.GetFloat();

#if !defined(SWDS)
		if ( demoplayer->IsPlayingBack() )
		{
			// adjust time scale if playing back demo
			fullscale *= demoplayer->GetPlaybackTimeScale();
		}
#endif

		host_frametime *= fullscale;

		host_frametime_unbounded = host_frametime;

#ifndef NO_TOOLFRAMEWORK
		if ( CommandLine()->CheckParm( "-tools" ) == NULL )
		{
#endif
			host_frametime = min( (double)host_frametime, MAX_FRAMETIME * fullscale);
#ifndef NO_TOOLFRAMEWORK
		}
#endif
	}
	else
#ifndef NO_TOOLFRAMEWORK
		if ( CommandLine()->CheckParm( "-tools" ) != NULL )
		{
			host_frametime_unbounded = host_frametime;
		}
		else
#endif // !NO_TOOLFRAMEWORK
	{	// don't allow really long or short frames
		host_frametime_unbounded = host_frametime;
		host_frametime = min( (double)host_frametime, MAX_FRAMETIME );
		host_frametime = max( (double)host_frametime, MIN_FRAMETIME );
	}
#endif

	// Adjust the client clock very slightly to keep it in line with the server clock.
	float adj = cl.GetClockDriftMgr().AdjustFrameTime( host_frametime ) - host_frametime;
	host_frametime += adj;
	host_frametime_unbounded += adj;

	if ( g_pSoundServices )									// not present on linux server
		g_pSoundServices->SetSoundFrametime(dt, host_frametime);

}

#define FPS_AVG_FRAC 0.9f

float g_fFramesPerSecond = 0.0f;

/*
==================
Host_PostFrameRate
==================
*/
void Host_PostFrameRate( float frameTime )
{
	frameTime = clamp( frameTime, 0.0001f, 1.0f );

	float fps = 1.0f / frameTime;
	g_fFramesPerSecond = g_fFramesPerSecond * FPS_AVG_FRAC + ( 1.0f - FPS_AVG_FRAC ) * fps;
}

/*
==================
Host_GetHostInfo
==================
*/
void Host_GetHostInfo(float *fps, int *nActive, int *nMaxPlayers, char *pszMap, int maxlen )
{
	// Count clients, report 
	int clients = sv.GetNumClients();

	*fps = g_fFramesPerSecond;
	*nActive = clients;

	if (pszMap)
	{
		if (sv.m_szMapname && sv.m_szMapname[0])
			Q_strncpy(pszMap, sv.m_szMapname, maxlen );
		else
			pszMap[0] = '\0';
	}

	*nMaxPlayers = sv.GetMaxClients();
}

static bool AppearsNumeric( char const *in )
{
	char const *p = in;
	int special[ 3 ];
	Q_memset( special, 0, sizeof( special ) );

	for ( ; *p; p++ )
	{
		if ( *p == '-' )
		{
			special[0]++;
			continue;
		}

		if ( *p == '+' )
		{
			special[1]++;
			continue;
		}

		if ( *p >= '0' && *p <= '9' )
		{
			continue;
		}

		if ( *p == '.' )
		{
			special[2]++;
			continue;
		}

		return false;
	}

	// Can't have multiple +, -, or decimals
	for ( int i = 0; i < 3; i++ )
	{
		if ( special[ i ] > 1 )
			return false;
	}

	// can't be + and - at same time
	if ( special[ 0 ] && special[ 1 ] )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: If the value is numeric, remove unnecessary trailing zeros
// Input  : *invalue - 
// Output : char const
//-----------------------------------------------------------------------------
char const * Host_CleanupConVarStringValue( char const *invalue )
{
	static char clean[ 256 ];

	Q_snprintf( clean, sizeof( clean ), "%s", invalue );

	// Don't mess with empty string
	// Otherwise, if it appears numeric and has a decimal, try to strip all zeroes after decimal
	if ( Q_strlen( clean ) >= 1 && AppearsNumeric( clean ) && Q_strstr( clean, "." ) )
	{
		char *end = clean + strlen( clean ) - 1;
		while ( *end && end >= clean )
		{
			// Removing trailing zeros
			if ( *end != '0' )
			{
				// Remove decimal, zoo
				if ( *end == '.' )
				{
					if ( end == clean )
					{
						*end = '0';
					}
					else
					{
						*end = 0;
					}
				}
				break;
			}

			*end-- = 0;
		}
	}

	return clean;
}

int Host_CountVariablesWithFlags( int flags, bool nonDefault )
{
	int i = 0;
	const ConCommandBase *var;

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

		const ConVar *pCvar = ( const ConVar * )var;

		if ( !pCvar->IsFlagSet( flags ) )
			continue;

		// It's == to the default value, don't count
		if ( nonDefault && !Q_strcasecmp( pCvar->GetDefault(), pCvar->GetString() ) )
			continue;

		i++;
	}

	return i;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : msg - 
//-----------------------------------------------------------------------------
void Host_BuildConVarUpdateMessage( NET_SetConVar *cvarMsg, int flags, bool nonDefault )
{
	int count = Host_CountVariablesWithFlags( flags, nonDefault );

	// Nothing to send
	if ( count <= 0 )
		return;

	// Too many to send, error out and have mod author get a clue.
	if ( count > 255 )
	{
		Sys_Error( "Engine only supports 255 ConVars marked %i\n", flags );
		return;
	}

	const ConCommandBase *var;

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

		const ConVar *pCvar = ( const ConVar * )var;

		if ( !pCvar->IsFlagSet( flags ) )
			continue;

		// It's == to the default value, don't count
		if ( nonDefault && !Q_strcasecmp( pCvar->GetDefault(), pCvar->GetString() ) )
			continue;

		NET_SetConVar::cvar_t acvar;

		Q_strncpy( acvar.name, pCvar->GetName(), MAX_OSPATH );
		Q_strncpy( acvar.value, Host_CleanupConVarStringValue( pCvar->GetString() ), MAX_OSPATH );

		cvarMsg->m_ConVars.AddToTail( acvar );
	}

	// Make sure this count matches original one!!!
	Assert( cvarMsg->m_ConVars.Count() == count );
}

#if !defined( SWDS )
// FIXME: move me somewhere more appropriate
void CL_SendVoicePacket(bool bFinal)
{
#if !defined( NO_VOICE )
	if ( !Voice_IsRecording() )
		return;

	CLC_VoiceData voiceMsg;

	// Get whatever compressed data there is and and send it.
	char uchVoiceData[2048];

	voiceMsg.m_DataOut.StartWriting( uchVoiceData, sizeof(uchVoiceData) );	

	voiceMsg.m_nLength = Voice_GetCompressedData( uchVoiceData, sizeof(uchVoiceData), bFinal ) * 8;

	if( !voiceMsg.m_nLength )
		return;

	voiceMsg.m_DataOut.SeekToBit( voiceMsg.m_nLength );	 // set correct writing position

	if ( cl.IsActive() )
	{
		cl.m_NetChannel->SendNetMsg( voiceMsg );
	}
#endif
}

#if defined ( _X360 )


void CL_ProcessXboxVoiceData()
{
	if ( Audio_GetXVoice() == NULL )
		return;

	if ( Audio_GetXVoice()->VoiceUpdateData() == true )
	{
		if ( cl.IsActive() )
		{
			Audio_GetXVoice()->VoiceSendData( cl.m_NetChannel );
		}
	}
}

#endif

void CL_ProcessVoiceData()
{
	VPROF_BUDGET( "CL_ProcessVoiceData", VPROF_BUDGETGROUP_OTHER_NETWORKING );

#if !defined( NO_VOICE )
	Voice_Idle(host_frametime);
	CL_SendVoicePacket(false);
#endif

#if defined ( _X360 )

	CL_ProcessXboxVoiceData();
#endif

}
#endif






/*
=====================
Host_UpdateScreen

Refresh the screen
=====================
*/
void Host_UpdateScreen( void )
{
#ifndef SWDS 

#ifdef _DEBUG
	if( r_ForceRestore.GetInt() )
	{
		ForceMatSysRestore();
		r_ForceRestore.SetValue(0);
	}
#endif // _DEBUG

	// Refresh the screen
	SCR_UpdateScreen ();
#endif
}

/*
====================
Host_UpdateSounds

Update sound subsystem and cd audio
====================
*/
void Host_UpdateSounds( void )
{
#if !defined( SWDS )
	// update audio
	if ( cl.IsActive() )
	{
		S_Update( &s_AudioState );	
	}
	else
	{
		S_Update( NULL );
	}
#endif
}

/*
==============================
Host_Speeds

==============================
*/
void CFrameTimer::ResetDeltas()
{
	for ( int i = 0; i < NUM_FRAME_SEGMENTS; i++ )
	{
		deltas[ i ] = 0.0f;
	}
}

void CFrameTimer::MarkFrame()
{
	double frameTime;
	double fps;

	// ConDMsg("%f %f %f\n", time1, time2, time3 );

	float fs_input = (deltas[FRAME_SEGMENT_INPUT])*1000.0;
	float fs_client = (deltas[FRAME_SEGMENT_CLIENT])*1000.0;
	float fs_server = (deltas[FRAME_SEGMENT_SERVER])*1000.0;
	float fs_render = (deltas[FRAME_SEGMENT_RENDER])*1000.0;
	float fs_sound = (deltas[FRAME_SEGMENT_SOUND])*1000.0;
	float fs_cldll = (deltas[FRAME_SEGMENT_CLDLL])*1000.0;
	float fs_exec = (deltas[FRAME_SEGMENT_CMD_EXECUTE])*1000.0;

	ResetDeltas();

	frameTime = host_frametime;
	//frameTime /= 1000.0;
	if ( frameTime < 0.0001 )
	{
		fps = 999.0;
	}
	else
	{
		fps = 1.0 / frameTime;
	}

	if (host_speeds.GetInt())
	{
		int ent_count = 0;
		int i;
		static int last_host_tickcount;

		int ticks = host_tickcount - last_host_tickcount;
		last_host_tickcount = host_tickcount;

		// count used entities
		for (i=0 ; i<sv.num_edicts ; i++)
		{
			if (!sv.edicts[i].IsFree())
				ent_count++;
		}

		char sz[ 256 ];
		Q_snprintf( sz, sizeof( sz ),
			"%3i fps -- inp(%.2f) sv(%.2f) cl(%.2f) render(%.2f) snd(%.2f) cl_dll(%.2f) exec(%.2f) ents(%d) ticks(%d)",
			(int)fps, 
			fs_input, 
			fs_server, 
			fs_client, 
			fs_render, 
			fs_sound, 
			fs_cldll, 
			fs_exec, 
			ent_count, 
			ticks );

#ifndef SWDS
		if ( host_speeds.GetInt() >= 2 )
		{
			Con_NPrintf ( 0, sz );
		}
		else
		{
			ConDMsg ( "%s\n", sz );
		}
#endif
	}

}

#define FRAME_TIME_FILTER_TIME 0.5f

void CFrameTimer::ComputeFrameVariability()
{
	m_pFrameTimeHistory[m_nFrameTimeHistoryIndex] = frametime;
	if ( ++m_nFrameTimeHistoryIndex >= FRAME_HISTORY_COUNT )
	{
		m_nFrameTimeHistoryIndex = 0;
	}

	// Compute a low-pass filter of the frame time over the last half-second
	// Count the number of samples that live within the last half-second
	int i = m_nFrameTimeHistoryIndex;
	int nMaxSamples = 0;
	float flTotalTime = 0.0f;
	while( (nMaxSamples < FRAME_HISTORY_COUNT) && (flTotalTime <= FRAME_TIME_FILTER_TIME) )
	{
		if ( --i < 0 )
		{
			i = FRAME_HISTORY_COUNT - 1;
		}		
		if ( m_pFrameTimeHistory[i] == 0.0f )
			break;

		flTotalTime += m_pFrameTimeHistory[i];
		++nMaxSamples;
	}

	if ( nMaxSamples == 0 )
	{
		m_flFPSVariability = 0.0f;
		m_flFPSStdDeviationSeconds = 0.0f;
		return;
	}

	float flExponent = -2.0f / (int)nMaxSamples;

	i = m_nFrameTimeHistoryIndex;
	float flAverageTime = 0.0f;
	float flExpCurveArea = 0.0f;
	int n = 0;
	while( n < nMaxSamples )
	{
		if ( --i < 0 )
		{
			i = FRAME_HISTORY_COUNT - 1;
		}
		flExpCurveArea += exp( flExponent * n );
		flAverageTime += m_pFrameTimeHistory[i] * exp( flExponent * n );
		++n;
	}

	flAverageTime /= flExpCurveArea;

	float flAveFPS = 0.0f;
	if ( flAverageTime != 0.0f )
	{
		flAveFPS = 1.0f / flAverageTime;
	}

	float flCurrentFPS = 0.0f;
	if ( frametime != 0.0f )
	{
		flCurrentFPS = 1.0f / frametime;
	}

	// Now subtract out the current fps to get variability in FPS
	m_flFPSVariability = fabs( flCurrentFPS - flAveFPS );

	// Now compute variance/stddeviation
	double sum = 0.0f;
	int count =0;
	for ( int j = 0; j < FRAME_HISTORY_COUNT; ++j )
	{
		if ( m_pFrameTimeHistory[ j ] == 0.0f )
			continue;

		double ft = min( (double)m_pFrameTimeHistory[ j ], 0.25 );

		sum += ft;
		++count;

	}

	if ( count <= 1 )
	{
		return;
	}

	double avg = sum / (double)count;
	double devSquared = 0.0f;
	for ( int j = 0; j < FRAME_HISTORY_COUNT; ++j )
	{
		if ( m_pFrameTimeHistory[ j ] == 0.0f )
			continue;

		double ft = min( (double)m_pFrameTimeHistory[ j ], 0.25 );

		double dt = ft - avg;

		devSquared += ( dt * dt );
	}

	double variance = devSquared / (double)( count - 1 );
	m_flFPSStdDeviationSeconds = sqrt( variance );
}

void Host_Speeds()
{
	g_HostTimes.MarkFrame();
#if !defined(SWDS)
	ToClientDemoPlayer( demoplayer )->MarkFrame( g_HostTimes.m_flFPSVariability );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: When singlestep == 1, then you must set next == 1 to run to the 
//  next frame.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool Host_ShouldRun( void )
{
	static int current_tick = -1;

	// See if we are single stepping
	if ( !singlestep.GetInt() )
	{
		return true;
	}

	// Did user set "next" to 1?
	if ( cvarNext.GetInt() )
	{
		// Did we finally finish this frame ( Host_ShouldRun is called in 3 spots to pause
		//  three different things ).
		if ( current_tick != (host_tickcount-1) )
		{
			// Okay, time to reset to halt execution again
			cvarNext.SetValue( 0 );
			return false;
		}

		// Otherwise, keep running this one frame since we are still finishing this frame
		return true;
	}
	else
	{
		// Remember last frame without "next" being reset ( time is locked )
		current_tick = host_tickcount;
		// Time is locked
		return false;
	}
}

static ConVar mem_periodicdumps( "mem_periodicdumps", "0", 0, "Write periodic memstats dumps every n seconds." );
static double g_flLastPeriodicMemDump = -1.0f;

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
static float g_TimeLastMemTest;
void Host_CheckDumpMemoryStats( void )
{
	if ( mem_test_each_frame.GetBool() )
	{
		if ( !MemAlloc_CrtCheckMemory() )
		{
			DebuggerBreakIfDebugging();
			Error( "Heap is corrupt\n" );
		}
	}
	else if ( mem_test_every_n_seconds.GetInt() > 0 )
	{
		float now = Plat_FloatTime();
		if ( now - g_TimeLastMemTest > mem_test_every_n_seconds.GetInt() )
		{
			g_TimeLastMemTest = now;
			if ( !MemAlloc_CrtCheckMemory() )
			{
				DebuggerBreakIfDebugging();
				Error( "Heap is corrupt\n" );
			}
		}
	}

	if ( mem_periodicdumps.GetFloat() > 0.0f )
	{
		double curtime = Plat_FloatTime();
		if ( curtime - g_flLastPeriodicMemDump > mem_periodicdumps.GetFloat() )
		{
			const char *pTest = sv.GetMapName();
			if ( !pTest || !pTest[0] )
			{
				// possibly at menu
				pTest = "nomap";
			}

			char mapname[ 256 ];
			Q_FileBase( pTest, mapname, sizeof( mapname ) );
#if defined( _MEMTEST )
			MemAlloc_SetStatsExtraInfo( pTest, "" );
#endif
			MemAlloc_DumpStatsFileBase( mapname );
			g_flLastPeriodicMemDump = curtime;
		}
	}


#if defined(_WIN32)
	if ( mem_dumpstats.GetInt() <= 0 )
		return;

	if ( mem_dumpstats.GetInt() == 1 )
		mem_dumpstats.SetValue( 0 ); // reset cvar, dump stats only once

	_CrtMemState state;
	Q_memset( &state, 0, sizeof( state ) );
	_CrtMemCheckpoint( &state );

	unsigned int size = 0;

	for ( int use = 0; use < _MAX_BLOCKS; use++)
	{
		size += state.lSizes[ use ];
	}
	Msg("MEMORY:  Run-time Heap\n------------------------------------\n");

	Msg( "\tHigh water %s\n", Q_pretifymem( state.lHighWaterCount,4 ) );
	Msg( "\tCurrent mem %s\n", Q_pretifymem( size,4 ) );
	Msg("------------------------------------\n");
	int hunk = Hunk_MallocSize();
	Msg("\tAllocated outside hunk:  %s\n", Q_pretifymem( size - hunk ) );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void _Host_SetGlobalTime()
{
	// Server
	g_ServerGlobalVariables.realtime			= realtime;
	g_ServerGlobalVariables.framecount			= host_framecount;
	g_ServerGlobalVariables.absoluteframetime	= host_frametime;
	g_ServerGlobalVariables.interval_per_tick	= host_state.interval_per_tick;
	g_ServerGlobalVariables.serverCount			= Host_GetServerCount();
#ifndef SWDS
	// Client
	g_ClientGlobalVariables.realtime			= realtime;
	g_ClientGlobalVariables.framecount			= host_framecount;
	g_ClientGlobalVariables.absoluteframetime	= host_frametime;
	g_ClientGlobalVariables.interval_per_tick	= host_state.interval_per_tick;
#endif
}

/*
==================
_Host_RunFrame

Runs all active servers
==================
*/

void _Host_RunFrame_Input( float accumulated_extra_samples, bool bFinalTick )
{
	VPROF_BUDGET( "_Host_RunFrame_Input", _T("Input") );

	// Run a test script?
	static bool bFirstFrame = true;
	if ( bFirstFrame )
	{
		bFirstFrame = false;
		const char *pScriptFilename = CommandLine()->ParmValue( "-testscript" );
		if ( pScriptFilename )
		{
			if ( !GetTestScriptMgr()->StartTestScript( pScriptFilename ) )
			{
				Error( "StartTestScript( %s ) failed.", pScriptFilename );
			}
		}
	}

	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_INPUT );

#ifndef SWDS
	// Client can process input
	ClientDLL_ProcessInput( );

	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CMD_EXECUTE );

	// process console commands
	Cbuf_Execute ();

	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CMD_EXECUTE );

	// Send any current movement commands to server and flush reliable buffer even if not moving yet.
	CL_Move( accumulated_extra_samples, bFinalTick );

#endif

	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_INPUT );
}

void _Host_RunFrame_Server( bool finaltick )
{
	VPROF_BUDGET( "_Host_RunFrame_Server", VPROF_BUDGETGROUP_GAME );
	VPROF_INCREMENT_COUNTER( "ticks", 1 );
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );

	// Run the Server frame ( read, run physics, respond )
	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_SERVER );
	SV_Frame ( finaltick );
	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_SERVER );

	// Look for connectionless rcon packets on dedicated servers
	// SV_CheckRcom(); TODO 
}

void _Host_RunFrame_Server_Async( int numticks )
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, numticks );

	for ( int tick = 0; tick < numticks; tick++ )
	{ 
		g_ServerGlobalVariables.tickcount = sv.m_nTickCount;
		g_ServerGlobalVariables.simTicksThisFrame = numticks - tick;
		bool bFinalTick = ( tick == (numticks - 1) );
		_Host_RunFrame_Server( bFinalTick );
	}
}


void _Host_RunFrame_Client( bool framefinished )
{
#ifndef SWDS
	VPROF( "_Host_RunFrame_Client" );
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, framefinished );

	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CLIENT );

	// Get any current state update from server, etc.
	CL_ReadPackets( framefinished );

#if defined( VOICE_OVER_IP )
	// Send any enqueued voice data to the server
	CL_ProcessVoiceData();
#endif // VOICE_OVER_IP

	cl.CheckUpdatingSteamResources();
	cl.CheckFileCRCsWithServer();

	// Resend connection request if needed.
	cl.RunFrame();

	if ( CL_IsHL2Demo() || CL_IsPortalDemo() ) // don't need sv.IsDedicated() because ded servers don't run this
	{
		void CL_DemoCheckGameUIRevealTime();
		CL_DemoCheckGameUIRevealTime();
	}

	Steam3Client().RunFrame();

	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CLIENT );

	// This takes 1 usec, so it's pretty cheap...
	CL_SetPagedPoolInfo();
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Used to set limits on certain convars in multiplayer/sv_cheats mode.
// Returns true if it was called recursively and it early-outed.
//-----------------------------------------------------------------------------
bool CheckVarRange_Generic( ConVar *pVar, int minVal, int maxVal )
{
	if ( !CanCheat() && !Host_IsSinglePlayerGame() )
	{
		int clampedValue = clamp( pVar->GetInt(), minVal, maxVal );
		if ( clampedValue != pVar->GetInt() )
		{
			Warning( "sv_cheats=0 prevented changing %s outside of the range [%d,%d] (was %d).\n", pVar->GetName(), minVal, maxVal, pVar->GetInt() );
			pVar->SetValue( clampedValue );
		}
	}

	return false;
}


void CheckSpecialCheatVars()
{
	static ConVar *mat_picmip = NULL;
	if ( !mat_picmip )
		mat_picmip = g_pCVar->FindVar( "mat_picmip" );

	CheckVarRange_r_rootlod();
	CheckVarRange_r_lod();
	HandleServerAllowColorCorrection();
}


void _Host_RunFrame_Render()
{
#ifndef SWDS
	VPROF( "_Host_RunFrame_Render" );
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "_Host_RunFrame_Render" );

	CheckSpecialCheatVars();

	int nOrgNoRendering = mat_norendering.GetInt();

	if ( cl_takesnapshot )
	{
		// turn off no-rendering mode, if taking screenshot
		mat_norendering.SetValue( 0 );
	}

	// update video if not running in background
	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_RENDER );

	CL_LatchInterpolationAmount();

	{
		VPROF( "_Host_RunFrame_Render - UpdateScreen" );
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "_Host_RunFrame_Render - UpdateScreen" );
		Host_UpdateScreen();
	}
	{
		VPROF( "_Host_RunFrame_Render - CL_DecayLights" );
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "_Host_RunFrame_Render - CL_DecayLights" );
		CL_DecayLights ();
	}

	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_RENDER );

	saverestore->OnFrameRendered();

#ifdef USE_SDL
	if ( g_pLauncherMgr )
	{
		g_pLauncherMgr->OnFrameRendered();
	}
#endif

	mat_norendering.SetValue( nOrgNoRendering );
#endif
}

void CL_FindInterpolatedAddAngle( float t, float& frac, AddAngle **prev, AddAngle **next )
{
	int c = cl.addangle.Count();

	*prev = NULL;
	*next = NULL;

	AddAngle *pentry = NULL;
	for ( int i = 0; i < c; i++ )
	{
		AddAngle *entry = &cl.addangle[ i ];

		*next = entry;

		// Time is earlier
		if ( t < entry->starttime )
		{
			if ( i == 0 )
			{
				*prev = *next;
				frac = 0.0f;
				return;
			}

			// Avoid div by zero
			if ( entry->starttime == pentry->starttime )
			{
				frac = 0.0f;
				return;
			}

			// Time spans the two entries
			frac = ( t - pentry->starttime ) / ( entry->starttime - pentry->starttime );
			frac = clamp( frac, 0.0f, 1.0f );
			return;
		}

		*prev = *next;
		pentry = entry;
	}
}

void CL_DiscardOldAddAngleEntries( float t )
{
	float killtime = t - host_state.interval_per_tick - 0.1f;

	for ( int i = 0; i < cl.addangle.Count(); i++ )
	{
		AddAngle *p = &cl.addangle[ i ];
		if ( p->starttime <= killtime )
		{
			cl.addangle.Remove( i );
			--i;
		}
	}

	// It's safe to reset the master counter once all the entries decay
	if ( cl.addangle.Count() == 0 )
	{
		cl.prevaddangletotal = cl.addangletotal = 0.0f;
	}
}

#ifndef SWDS
void CL_ApplyAddAngle()
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );

	float curtime = cl.GetTime() - host_state.interval_per_tick;

	AddAngle *prev = NULL, *next = NULL;
	float frac = 0.0f;

	float addangletotal = 0.0f;

	CL_FindInterpolatedAddAngle( curtime, frac, &prev, &next );

	if ( prev && next )
	{
		addangletotal = prev->total + frac * ( next->total - prev->total );
	}
	else
	{ 
		addangletotal = cl.prevaddangletotal;
	}

	float amove = addangletotal - cl.prevaddangletotal;

	// Update view angles
	cl.viewangles[ 1 ] += amove;
	// Update client .dll view of angles
	g_pClientSidePrediction->SetLocalViewAngles( cl.viewangles );

	// Remember last total
	cl.prevaddangletotal = addangletotal;

	CL_DiscardOldAddAngleEntries( curtime );
}
#endif

void _Host_RunFrame_Sound()
{
#ifndef SWDS

	VPROF_BUDGET( "_Host_RunFrame_Sound", VPROF_BUDGETGROUP_OTHER_SOUND );

	g_HostTimes.StartFrameSegment( FRAME_SEGMENT_SOUND );

	Host_UpdateSounds();

	g_HostTimes.EndFrameSegment( FRAME_SEGMENT_SOUND );
#endif
}

float Host_GetSoundDuration( const char *pSample )
{
#ifndef SWDS
	if (!sv.IsDedicated())
	{
		extern float SV_GetSoundDuration(const char *pSample);
		extern float AudioSource_GetSoundDuration(CSfxTable *pSfx);
		int index = cl.LookupSoundIndex(pSample);
		if (index >= 0)
			return AudioSource_GetSoundDuration(cl.GetSound(index));
		return SV_GetSoundDuration(pSample);
	}
#endif
	return 0.0f;
}

CON_COMMAND( host_runofftime, "Run off some time without rendering/updating sounds\n" )
{
	if ( args.ArgC() != 2 )
	{
		ConMsg( "Usage:  host_runofftime <seconds>\n" );
		return;
	}

	if ( !sv.IsActive() )
	{
		ConMsg( "host_ruofftime:  must be running a server\n" );
		return;
	}

	if ( sv.IsMultiplayer() )
	{
		ConMsg( "host_ruofftime:  only valid in single player\n" );
		return;
	}

	float advanceTime = atof( args[1] );
	if ( advanceTime <= 0.0f )
		return;

	// 15 minutes is a _long_ time!!!
	if ( advanceTime > 15.0f * 60.0f )
	{
		ConMsg( "host_runofftime would run off %.2f minutes!!! ignoring\n",
			advanceTime / 60.0f );
		return;
	}

	ConMsg( "Skipping ahead for %f seconds\n", advanceTime );

	SCR_UpdateScreen();
	SCR_UpdateScreen ();
}

#if !defined( _X360 )
S_API int SteamGameServer_GetIPCCallCount();
#else
S_API int SteamGameServer_GetIPCCallCount() { return 0; }
#endif
void Host_ShowIPCCallCount()
{
	// If set to 0 then get out.
	if ( host_ShowIPCCallCount.GetInt() == 0 )
		return;
	
	static float s_flLastTime = 0;
	static int s_nLastTick = host_tickcount;
	static int s_nLastFrame = host_framecount;
	
	// Figure out how often they want to update.
	double flInterval = 0;
	if ( host_ShowIPCCallCount.GetFloat() > 0 )
	{
		flInterval = 1.0f / host_ShowIPCCallCount.GetFloat();
	}
	
	// This is called every frame so increment the frame counter.
	double flCurTime = Plat_FloatTime();
	if ( flCurTime - s_flLastTime >= flInterval )
	{
		uint32 callCount;
		ISteamClient *pSteamClient = SteamClient();
		if ( pSteamClient )
		{
			callCount = pSteamClient->GetIPCCallCount();
		}
		else
		{
			// Ok, we're a dedicated server and we need to use this to get it.
			callCount = (uint32)SteamGameServer_GetIPCCallCount();
		}

		// Avoid a divide by zero.
		int frameCount = host_framecount - s_nLastFrame;
		int tickCount = host_tickcount - s_nLastTick;
		if ( frameCount == 0 || tickCount == 0 )
			return;
			
		Msg( "host_ShowIPCCallCount: %d IPC calls in the past [%d frames, %d ticks]  Avg: [%.2f/frame, %.2f/tick]\n", 
			callCount, frameCount, tickCount, (float)callCount / frameCount, (float)callCount / tickCount );
			
		s_flLastTime = flCurTime;
		s_nLastTick = host_tickcount;
		s_nLastFrame = host_framecount;
	}
}

void Host_SetClientInSimulation( bool bInSimulation )
{
#ifndef SWDS
	// Tracker 77931:  If the game is paused, then lock the client clock at the previous tick boundary 
	//  (otherwise we'll keep interpolating through the "remainder" time causing the paused characters
	//  to twitch like they have the shakes)
	// TODO:  Since this rounds down on the frame we paused, we could see a slight backsliding.  We could remember the last "remainder" before pause and re-use it and 
	//  set insimulation == false to be mroe exact.  We'd still have to deal with the timing difference between
	//  when pause/unpause happens on the server versus the client
	cl.insimulation = bInSimulation || cl.IsPaused();

	// Compute absolute/render time stamp
	g_ClientGlobalVariables.curtime = cl.GetTime();
	g_ClientGlobalVariables.frametime = cl.GetFrameTime();
#endif
}

static ConVar host_Sleep( "host_sleep", "0", FCVAR_CHEAT, "Force the host to sleep a certain number of milliseconds each frame." );
extern ConVar sv_alternateticks;
#define LOG_FRAME_OUTPUT 0

void _Host_RunFrame (float time)
{
	MDLCACHE_COARSE_LOCK_(g_pMDLCache);
	static double host_remainder = 0.0f;
	double prevremainder;
    bool shouldrender;

#if defined( RAD_TELEMETRY_ENABLED )
	if( g_Telemetry.DemoTickEnd == ( uint32 )-1 )
	{
		Cbuf_AddText( "quit\n" );
	}
#endif
    int numticks;
	{
		// Profile scope specific to the top of this function, protect from setjmp() problems
		VPROF( "_Host_RunFrame_Upto_MarkFrame" );

		if ( host_checkheap )
		{
#if defined(_WIN32)
			if ( _heapchk() != _HEAPOK )
			{
				Sys_Error( "_Host_RunFrame (top):  _heapchk() != _HEAPOK\n" );
			}
#endif
		}

		// When playing back a VCR file, don't do host_sleep. That way, if it was recorded with
		// host_sleep on, it'll play back way Faster.
		if ( host_Sleep.GetInt() && VCRGetMode() != VCR_Playback )
		{
			Sys_Sleep( host_Sleep.GetInt() );
		}

		// Slow down the playback?	
		if ( g_iVCRPlaybackSleepInterval )
		{
			Sys_Sleep( g_iVCRPlaybackSleepInterval );
		}

		MapReslistGenerator().RunFrame();

		static int lastrunoffsecond = -1;

		if ( setjmp ( host_enddemo ) )
			return;			// demo finished.

		// decide the simulation time
		Host_AccumulateTime ( time );
		_Host_SetGlobalTime();

		shouldrender = !sv.IsDedicated();

		// FIXME:  Could track remainder as fractional ticks instead of msec
		prevremainder = host_remainder;
		if ( prevremainder < 0 )
			prevremainder = 0;

	#if !defined(SWDS)
		if ( !demoplayer->IsPlaybackPaused() )
	#endif
		{
			host_remainder += host_frametime;
		}

		numticks = 0;	// how many ticks we will simulate this frame
		if ( host_remainder >= host_state.interval_per_tick )
		{
			numticks = (int)( floor( host_remainder / host_state.interval_per_tick ) );

			// round to nearest even ending tick in alternate ticks mode so the last
			// tick is always simulated prior to updating the network data
			// NOTE: alternate ticks only applies in SP!!!
			if ( Host_IsSinglePlayerGame() &&
				sv_alternateticks.GetBool() )
			{
				int startTick = g_ServerGlobalVariables.tickcount;
				int endTick = startTick + numticks;
				endTick = AlignValue( endTick, 2 );
				numticks = endTick - startTick;
			}

			host_remainder -= numticks * host_state.interval_per_tick;
		}

        cl.m_ClockDriftMgr.m_nNumberOfTicks = numticks;

		host_nexttick = host_state.interval_per_tick - host_remainder;

		g_pMDLCache->MarkFrame();
	}
#ifndef SWDS
    const auto CalcInterpolationAmount = [&]()
    {
        // TODO_ENHANCED:
        // This check permits to fix interpolation problems on the
        // local player that valve has been (fucking finally)
        // caring about on counter-strike 2.
        //
        // To recall the original issue, the
        // problem that Valve cared about is that interpolation
        // had some problems with interpolating the local
        // player because the screen would never in the first
        // place match the tick "screen", because interpolation
        // amount could never reach 0.0 or 1.0
        //
        // Valve solution was to introduce bugs with lag
        // compensating the local player and made the game worse,
        // introducing a new way for cheaters to cheat even more
        // on their games.
        // I'm joking, but you can clearly see the outcome anyway.
        //
        // My solution is to simply set interpolation amount
        // to 0.0 when a tick arrives.
        //
        // So when we shoot, we get the frame we shot with an
        // interpolation amount at 0.0, perfectly aligned to user
        // commands which is ideal for us.
        //
        // README_ENHANCED:
        // Unfortunately, some players still notice it with low enough fps.
        // This is a problem; we return then to lag compensating the local player.
        // Two choices here:
        //
        // 1) For precision we might need to send camera position, that is proven to work.
        // 2) Send interpolation_amount so we calculate it server and in runcommand.
        //
		// Both works, but the first one requires validation,
		// the second doesn't at the expense of some unprecisions due to floats.
		// The first one is the easier route to avoid issues.

        g_ClientGlobalVariables.interpolation_amount = cl.m_tickRemainder / host_state.interval_per_tick;
    };
#endif
    {
		// Profile scope, protect from setjmp() problems
		VPROF( "_Host_RunFrame" );
		tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "_Host_RunFrame" );

		g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CMD_EXECUTE );

		// process console commands
		Cbuf_Execute ();

		// initialize networking for dedicated server after commandline & autoexec.cfg have been parsed
		if ( NET_IsDedicated() && !NET_IsMultiplayer() )
			NET_SetMutiplayer( true );

		g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CMD_EXECUTE );

		// Msg( "Running %i ticks (%f remainder) for frametime %f total %f tick %f delta %f\n", numticks, remainder, host_frametime, host_time );
		g_ServerGlobalVariables.interpolation_amount = 0.0f;
#ifndef SWDS
		// g_ClientGlobalVariables.interpolation_amount = 0.0f;

		cl.insimulation = true;
#endif

		host_frameticks = numticks;
		host_currentframetick = 0;

#if !defined( SWDS )
		// This is to make the tool do both sim + rendering on the initial frame
		// cl.IsActive changes in the loop below, as does scr_nextdrawtick
		// We're just caching off the state here so that we have a consistent return value
		// for enginetool->IsInGame the entire frame
		g_pEngineToolInternal->SetIsInGame( cl.IsActive() && ( scr_nextdrawtick == 0 ) );
#endif
		CJob *pGameJob = NULL;

// threaded path only supported in listen server
#ifndef SWDS
		if ( !IsEngineThreaded() )
#endif
		{
#ifndef SWDS
			if ( g_ClientDLL )
			{
				g_ClientDLL->IN_SetSampleTime(host_frametime);
			}
			g_ClientGlobalVariables.simTicksThisFrame = 1;
#endif
			cl.m_tickRemainder = host_remainder;
			g_ServerGlobalVariables.simTicksThisFrame = 1;
            cl.SetFrameTime(host_frametime);
#ifndef SWDS
            g_ClientGlobalVariables.next_interpolation_amount = cl.m_tickRemainder / host_state.interval_per_tick;

			// TODO_ENHANCED:
			// Update the mouse as last so we can get the right viewangles while taking screenshot.
			// The mouse is always simulated for the current frame's time
			// This makes updates smooth in every case
			// continuous controllers affecting the view are also simulated this way
			// but they have a cap applied by IN_SetSampleTime() so they are not also
			// simulated during input gathering
			g_ClientGlobalVariables.frametime = host_frametime;
			CL_ExtraMouseUpdate( host_frametime );
#endif
			for ( int tick = 0; tick < numticks; tick++ )
            {
                g_ServerGlobalVariables.currenttick = tick;
#ifndef SWDS
                g_ClientGlobalVariables.currenttick = tick;
#endif
				// Emit an ETW event every simulation frame.
				ETWSimFrameMark( sv.IsDedicated() );

				double now = Plat_FloatTime();
				float jitter = now - host_idealtime;

				// Track jitter (delta between ideal time and actual tick execution time)
				host_jitterhistory[ host_jitterhistorypos ] = jitter;
				host_jitterhistorypos = ( host_jitterhistorypos + 1 ) % ARRAYSIZE(host_jitterhistory);

				// Very slowly decay "ideal" towards current wall clock unless delta is large
				if ( fabs( jitter ) > 1.0f )
				{
					host_idealtime = now;
				}
				else
				{
					host_idealtime = 0.99 * host_idealtime + 0.01 * now;
				}

				// process any asynchronous network traffic (TCP), set net_time
				NET_RunFrame( now );

				// Only send updates on final tick so we don't re-encode network data multiple times per frame unnecessarily
				bool bFinalTick = ( tick == (numticks - 1) );

				// initialize networking for dedicated server after commandline & autoexec.cfg have been parsed
				if ( NET_IsDedicated() && !NET_IsMultiplayer() )
					NET_SetMutiplayer( true );

				g_ServerGlobalVariables.tickcount = sv.m_nTickCount;
				// NOTE:  Do we want do this at start or end of this loop?
				++host_tickcount;
				++host_currentframetick;
#ifndef SWDS
                g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();

				// Make sure state is correct
				CL_CheckClientState();
#endif
				//---------------------------------------------------------
				// Run prediction, useful when fps is lower than tickrate.
				//---------------------------------------------------------
				// CL_RunPrediction( PREDICTION_NORMAL );

				_Host_RunFrame_Input( prevremainder, bFinalTick );
				prevremainder = 0;

				//-------------------
				//
				// server operations
				//
				//-------------------

				_Host_RunFrame_Server( bFinalTick );

				// Additional networking ops for SPLITPACKET stuff (99.9% of the time this will be an empty list of work)
				NET_SendQueuedPackets();

				//-------------------
				//
				// client operations
				//
				//-------------------
#ifndef SWDS
				if ( !sv.IsDedicated() )
                {
                    _Host_RunFrame_Client(bFinalTick);
				}

				toolframework->Think( bFinalTick );
#endif

				host_idealtime += host_state.interval_per_tick;
			}
			
			// run HLTV if active
			if ( hltv )
			{
				tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "hltv->RunFrame()" );
				hltv->RunFrame();
			}

			if ( hltvtest )
			{
				tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "hltvtest->RunFrame()" );
				hltvtest->RunFrame();
			}

#if defined( REPLAY_ENABLED )
			// run replay if active
			if ( replay )
			{
				replay->RunFrame();
			}

			// Update server-side replay history manager
			if ( sv.IsDedicated() && g_pServerReplayContext && g_pServerReplayContext->IsInitialized() )
			{
				g_pServerReplayContext->Think();
			}
#endif

#ifndef SWDS
			// This is a hack to let timedemo pull messages from the queue faster than every 15 msec
			// Also when demoplayer is skipping packets to a certain tick we should process the queue
			// as quickly as we can.
			if ( numticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) )
			{
                _Host_RunFrame_Client(true);
			}

			if ( !sv.IsDedicated() )
			{
				tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "Host_SetClientInSimulation" );

				// This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder)
                Host_SetClientInSimulation(false);

				CalcInterpolationAmount();

    #if defined(REPLAY_ENABLED)
                // Update client-side replay history manager - called here
                // since interpolation_amount is set
                if ( g_pClientReplayContext && g_pClientReplayContext->IsInitialized() )
				{
					g_pClientReplayContext->Think();
				}
    #endif
                //-------------------
                // Run prediction if it hasn't been run yet
                //-------------------
                // If we haven't predicted/simulated the player
                // (multiplayer with prediction enabled and
                //  not a listen server with zero frame lag, then go ahead
                //  and predict now
                CL_RunPrediction( PREDICTION_NORMAL );

				CL_ApplyAddAngle();
			}
#endif
#if defined( REPLAY_ENABLED )
			// Let the replay system think
			if ( g_pReplay )
			{
				g_pReplay->Think();
			}
#endif
#if LOG_FRAME_OUTPUT
			if ( !cl.IsPaused() || !sv.IsPaused() )
			{
				Msg("=============SIM: CLIENT %5d + %d, SERVER %5d + %d\t REM: %.2f\n", cl.GetClientTickCount(), numticks, sv.m_nTickCount, numticks, host_remainder*1000.0f );
			}
#endif
		}
#ifndef SWDS
		else
		{
			static int numticks_last_frame = 0;
			static float host_remainder_last_frame = 0, prev_remainder_last_frame = 0, last_frame_time = 0;

			int clientticks;
			int serverticks;

			clientticks = numticks_last_frame;
			cl.m_tickRemainder = host_remainder_last_frame;
            g_ClientGlobalVariables.next_interpolation_amount = cl.m_tickRemainder / host_state.interval_per_tick;
			cl.SetFrameTime( last_frame_time );
			if ( g_ClientDLL )
			{
				g_ClientDLL->IN_SetSampleTime(last_frame_time);
			}

			last_frame_time = host_frametime;

			serverticks = numticks;
			g_ClientGlobalVariables.simTicksThisFrame = clientticks;
			g_ServerGlobalVariables.simTicksThisFrame = serverticks;
			g_ServerGlobalVariables.tickcount		  = sv.m_nTickCount;

			// TODO_ENHANCED:
			// Update the mouse as last so we can get the right viewangles while taking screenshot.
			// The mouse is always simulated for the current frame's time
			// This makes updates smooth in every case
			// continuous controllers affecting the view are also simulated this way
			// but they have a cap applied by IN_SetSampleTime() so they are not also
			// simulated during input gathering
			g_ClientGlobalVariables.frametime = host_frametime;
			CL_ExtraMouseUpdate( host_frametime );

			// THREADED: Run Client
			// -------------------
			for ( int tick = 0; tick < clientticks; tick++ )
            {
                g_ServerGlobalVariables.currenttick = tick;
                g_ClientGlobalVariables.currenttick = tick;
				// process any asynchronous network traffic (TCP), set net_time
				NET_RunFrame(  Plat_FloatTime() );

				// Only send updates on final tick so we don't re-encode network data multiple times per frame unnecessarily
				bool bFinalTick = ( tick == (clientticks - 1) );

				// initialize networking for dedicated server after commandline & autoexec.cfg have been parsed
				if ( NET_IsDedicated() && !NET_IsMultiplayer() )
					NET_SetMutiplayer( true );
                g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();

				// Make sure state is correct
				CL_CheckClientState();
				// Additional networking ops for SPLITPACKET stuff (99.9% of the time this will be an empty list of work)
				NET_SendQueuedPackets();
				//-------------------
				//
				// client operations
				//
				//-------------------
				if ( !sv.IsDedicated() )
				{
                    _Host_RunFrame_Client(bFinalTick);
				}
				toolframework->Think( bFinalTick );
			}
			// This is a hack to let timedemo pull messages from the queue faster than every 15 msec
			// Also when demoplayer is skipping packets to a certain tick we should process the queue
			// as quickly as we can.
			if ( clientticks == 0 && ( demoplayer->IsPlayingTimeDemo() || demoplayer->IsSkipping() ) )
            {
                g_ServerGlobalVariables.currenttick = 0;
                g_ClientGlobalVariables.currenttick = 0;
				_Host_RunFrame_Client( true );
			}

			// This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder)
			Host_SetClientInSimulation( false );

			CalcInterpolationAmount();

			//-------------------
			// Run prediction if it hasn't been run yet
			//-------------------
			// If we haven't predicted/simulated the player (multiplayer with prediction enabled and
			//  not a listen server with zero frame lag, then go ahead and predict now
			CL_RunPrediction( PREDICTION_NORMAL );

			CL_ApplyAddAngle();

			Host_SetClientInSimulation( true );

			// THREADED: Run Input
			// -------------------
			int saveTick = g_ClientGlobalVariables.tickcount;

			for ( int tick = 0; tick < serverticks; tick++ )
            {
                g_ServerGlobalVariables.currenttick = tick;
                g_ClientGlobalVariables.currenttick = tick;
				// NOTE:  Do we want do this at start or end of this loop?
				++host_tickcount;
				++host_currentframetick;
				g_ClientGlobalVariables.tickcount = host_tickcount;
				bool bFinalTick = tick==(serverticks-1) ? true : false;
				// Run prediction before inputs if fps is lower than tickrate
				// CL_RunPrediction( PREDICTION_NORMAL );
				_Host_RunFrame_Input( prevremainder, bFinalTick );
				prevremainder = 0;
				// process any asynchronous network traffic (TCP), set net_time
				NET_RunFrame(  Plat_FloatTime() );
			}

			Host_SetClientInSimulation( false );

			g_ClientGlobalVariables.tickcount = saveTick;
			numticks_last_frame = numticks;
			host_remainder_last_frame = host_remainder;

			// THREADED: Run Server
			// -------------------
			// set net_time once before running the server
			NET_SetTime( Plat_FloatTime() );
			pGameJob = new CFunctorJob( CreateFunctor( _Host_RunFrame_Server_Async, serverticks ) );
			if ( IsX360() )
			{
				pGameJob->SetServiceThread( g_nServerThread );
			}
			g_pThreadPool->AddJob( pGameJob );
#if LOG_FRAME_OUTPUT
			if ( !cl.IsPaused() || !sv.IsPaused() )
			{
				Msg("=============SIM: CLIENT %5d + %d, SERVER %5d + %d\t REM: %.2f\n", cl.GetClientTickCount(), clientticks, sv.m_nTickCount, serverticks, host_remainder*1000.0f );
			}
#endif
		}
#endif	// SWDS

		g_Log.RunFrame();

		if ( shouldrender )
		{
#if LOG_FRAME_OUTPUT
			if ( !cl.IsPaused() || !sv.IsPaused() )
			{
				static float lastFrameTime = 0;
				float frametime = g_ClientGlobalVariables.curtime - lastFrameTime;
				Msg("RENDER AT: %6.4f: %.2fms [%.2fms implicit] frametime\n", 
					g_ClientGlobalVariables.curtime, g_ClientGlobalVariables.frametime*1000.0f, frametime * 1000.0f);
				lastFrameTime = g_ClientGlobalVariables.curtime;
			}
#endif
			//-------------------
			// rendering
			//-------------------
			_Host_RunFrame_Render();

			//-------------------
			// sound
			//-------------------
			_Host_RunFrame_Sound();

#ifndef DEDICATED
			if ( g_bVCRSingleStep )
			{
				VCR_EnterPausedState();
			}
#endif
		}
		else
		{
			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "modelloader->UpdateDynamicModels" );
			VPROF( "UpdateDynamicModels" );
			CMDLCacheCriticalSection critsec( g_pMDLCache );
			modelloader->UpdateDynamicModels();
		}

		//-------------------
		// simulation
		//-------------------
		g_HostTimes.MarkSwapTime( );
#ifndef SWDS
		if ( !sv.IsDedicated() )
		{
			VPROF( "_Host_RunFrame - ClientDLL_Update" );
			tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "_Host_RunFrame - ClientDLL_Update" );

			// Client-side simulation
			g_HostTimes.StartFrameSegment( FRAME_SEGMENT_CLDLL );

			ClientDLL_Update();

			g_HostTimes.EndFrameSegment( FRAME_SEGMENT_CLDLL );
		}
#endif
		if ( pGameJob )
		{
			{
				VPROF_BUDGET( "WaitForAsyncServer", "AsyncServer" );
				if ( Host_IsSinglePlayerGame() )
				{
					// This should change to a YieldWait if the server starts wanting to parallel process. If
					// so, will need some route for the server to queue up work it wants to execute outside
					// its frame, otherwise some of it would be performed during the yield. Right now
					// need to wait for server so we don't stall on queued AI operations (toml 7/3/2007)
					pGameJob->ExecuteAndRelease();
				}
				else
				{
					pGameJob->WaitForFinishAndRelease();
				}
			}
			SV_FrameExecuteThreadDeferred();
		}

		//-------------------
		// time
		//-------------------

		Host_Speeds();

		Host_UpdateMapList();

		host_framecount++;
#if !defined(SWDS)
		if ( !demoplayer->IsPlaybackPaused() )
#endif
		{
			host_time = host_tickcount * host_state.interval_per_tick + cl.m_tickRemainder;
		}

		Host_PostFrameRate( host_frametime );

		if ( host_checkheap )
		{
#ifdef _WIN32
			if ( _heapchk() != _HEAPOK )
			{
				Sys_Error( "_Host_RunFrame (bottom):  _heapchk() != _HEAPOK\n" );
			}
#endif
		}

		Host_CheckDumpMemoryStats();

		GetTestScriptMgr()->CheckPoint( "frame_end" );
	} // Profile scope, protect from setjmp() problems

    Host_ShowIPCCallCount();
}
/*
==============================
Host_Frame

==============================
*/
void Host_RunFrame( float time )
{
	static  double	timetotal = 0;
	static  int		timecount = 0;
	static	double  timestart = 0;

#ifndef SWDS
	if ( !scr_drawloading && sv.IsActive() && cl.IsActive() && !sv.m_bLoadgame)
	{
		switch ( host_thread_mode.GetInt() )
		{
		case HTM_DISABLED:	g_bThreadedEngine = false;									break;
		case HTM_DEFAULT:	g_bThreadedEngine = ( g_pThreadPool->NumThreads() > 0 );	break;
		case HTM_FORCED:	g_bThreadedEngine = true;									break;
		}
	}
	else
#endif
	{
		g_bThreadedEngine = false;
	}

	if ( !host_profile.GetBool() )
	{
		_Host_RunFrame( time );
		return;
	}

	double	time1 = Sys_FloatTime();

	_Host_RunFrame( time );

	double	time2 = Sys_FloatTime();

	timetotal += time2 - time1; // time in seconds
	timecount++;

	if (timecount < 1000)
		return;

	float fps = 1000/(time2 - timestart);

	ConMsg ("host_profile : %i clients, %.1f msec, %.1f fps\n",  
		sv.GetNumClients(),  timetotal, fps );

	timecount = 0;
	timetotal = 0;
	timestart = time2;
}


//-----------------------------------------------------------------------------
// A more secure means of enforcing low violence.
//-----------------------------------------------------------------------------
bool IsLowViolence_Secure()
{
#ifndef DEDICATED
	if ( !IsX360() && Steam3Client().SteamApps() )
	{
		// let Steam determine current violence settings 		
		return Steam3Client().SteamApps()->BIsLowViolence();
	}
	else if ( IsX360() )
	{
		// Low violence for the 360 is enabled by the presence of a file.
		if ( g_pFileSystem->FileExists( "cfg/violence.cfg" ) )
		{
			return true;
		}
		
		return false;
	}
#endif
		
	return false;
}


//-----------------------------------------------------------------------------
// If "User Token 2" exists in HKEY_CURRENT_USER/Software/Valve/Half-Life/Settings
// then we disable gore. Obviously not very secure.
//-----------------------------------------------------------------------------
bool IsLowViolence_Registry()
{
	char szSubKey[128];
	int nBufferLen;
	char szBuffer[128];
	bool bReducedGore = false;

	memset( szBuffer, 0, 128 );

	char const *appname = "Source";
	Q_snprintf(szSubKey, sizeof( szSubKey ), "Software\\Valve\\%s\\Settings", appname );

	nBufferLen = 127;
	Q_strncpy( szBuffer, "", sizeof( szBuffer ) );

	Sys_GetRegKeyValue( szSubKey, "User Token 2", szBuffer,	nBufferLen, szBuffer );

	// Gore reduction active?
	bReducedGore = ( Q_strlen( szBuffer ) > 0 ) ? true : false;
	if ( !bReducedGore )
	{
		Sys_GetRegKeyValue( szSubKey, "User Token 3", szBuffer, nBufferLen, szBuffer );

		bReducedGore = ( Q_strlen( szBuffer ) > 0 ) ? true : false;
	}

	char gamedir[MAX_OSPATH];
	Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );

	// also check mod specific directories for LV changes
	Q_snprintf(szSubKey, sizeof( szSubKey ), "Software\\Valve\\%s\\%s\\Settings", appname, gamedir );

	nBufferLen = 127;
	Q_strncpy( szBuffer, "", sizeof( szBuffer ) );

	Sys_GetRegKeyValue( szSubKey, "User Token 2", szBuffer,	nBufferLen, szBuffer );
	if ( Q_strlen( szBuffer ) > 0 )
	{
		bReducedGore = true;
	}

	Sys_GetRegKeyValue( szSubKey, "User Token 3", szBuffer,	nBufferLen, szBuffer );
	if ( Q_strlen( szBuffer ) > 0 )
	{
		bReducedGore = true;
	}
	
	return bReducedGore;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Host_CheckGore( void )
{
	bool bLowViolenceRegistry = false;
	bool bLowViolenceSecure = false;

	//
	// First check the old method of enabling low violence via the registry.
	//
#ifdef WIN32
	bLowViolenceRegistry = IsLowViolence_Registry();
#endif
	//
	// Next check the new method of enabling low violence based on country of purchase
	// and other means that are inaccessible by the user.
	//
	if ( GetCurrentMod() && Q_stricmp( GetCurrentMod(), "cstrike" ) != 0 )
		bLowViolenceSecure = IsLowViolence_Secure();

	//
	// If either method says "yes" to low violence, we're in low violence mode.
	//
	if ( bLowViolenceRegistry || bLowViolenceSecure )
	{
		g_bLowViolence = true;
		
		if ( bLowViolenceRegistry )
		{
			violence_hblood.SetValue( 0 );
			violence_hgibs.SetValue( 0 );
			violence_ablood.SetValue( 0 );
			violence_agibs.SetValue( 0 );
		}
	}
	else
	{
		g_bLowViolence = false;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Host_InitProcessor( void )
{
	const CPUInformation& pi = *GetCPUInformation();

	// Compute Frequency in Mhz: 
	char* szFrequencyDenomination = "Mhz";
	double fFrequency = pi.m_Speed / 1000000.0;

	// Adjust to Ghz if nessecary:
	if( fFrequency > 1000.0 )
	{
		fFrequency /= 1000.0;
		szFrequencyDenomination = "Ghz";
	}

	char szFeatureString[256];
	Q_strncpy( szFeatureString, pi.m_szProcessorID, sizeof( szFeatureString ) );
	Q_strncat( szFeatureString, " ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );

	if( pi.m_bSSE )
	{
		if( MathLib_SSEEnabled() ) Q_strncat(szFeatureString, "SSE ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
		else					   Q_strncat(szFeatureString, "(SSE) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	}

	if( pi.m_bSSE2 )
	{
		if( MathLib_SSE2Enabled() ) Q_strncat(szFeatureString, "SSE2 ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
		else					   Q_strncat(szFeatureString, "(SSE2) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	}

	if( pi.m_bMMX )
	{
		if( MathLib_MMXEnabled() ) Q_strncat(szFeatureString, "MMX ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
		else					   Q_strncat(szFeatureString, "(MMX) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	}

	if( pi.m_b3DNow )
	{
		if( MathLib_3DNowEnabled() ) Q_strncat(szFeatureString, "3DNow ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
		else					   Q_strncat(szFeatureString, "(3DNow) ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	}

	if( pi.m_bRDTSC )	Q_strncat(szFeatureString, "RDTSC ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	if( pi.m_bCMOV )	Q_strncat(szFeatureString, "CMOV ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );
	if( pi.m_bFCMOV )	Q_strncat(szFeatureString, "FCMOV ", sizeof( szFeatureString ), COPY_ALL_CHARACTERS );

	// Remove the trailing space.  There will always be one.
	szFeatureString[Q_strlen(szFeatureString)-1] = '\0';

	// Dump CPU information:
	if( pi.m_nLogicalProcessors == 1 )
	{
		ConDMsg( "1 CPU, Frequency: %.01f %s,  Features: %s\n", 
			fFrequency,
			szFrequencyDenomination,
			szFeatureString
			);
	} 
	else
	{
		char buffer[256] = "";
		if( pi.m_nPhysicalProcessors != pi.m_nLogicalProcessors )
		{
			Q_snprintf(buffer, sizeof( buffer ), " (%i physical)", (int) pi.m_nPhysicalProcessors );
		}

		ConDMsg( "%i CPUs%s, Frequency: %.01f %s,  Features: %s\n", 
			(int)pi.m_nLogicalProcessors,
			buffer,
			fFrequency,
			szFrequencyDenomination,
			szFeatureString
			);
	}

#if defined( _WIN32 )
	if ( s_bInitPME )
	{
		// Initialize the performance monitoring events code.
		InitPME();
	}
#endif
}

//-----------------------------------------------------------------------------
// Specifically used by the model loading code to mark models
// touched by the current map
//-----------------------------------------------------------------------------
int Host_GetServerCount( void )
{
	if (cl.m_nSignonState >= SIGNONSTATE_NEW)
	{
		// the server count cannot be relied on until the server info message
		// the new state guarantees its validity
		return cl.m_nServerCount;
	}
	else if (sv.m_State >= ss_loading)
	{
		return sv.GetSpawnCount();
	}

	// this is unfortunate, and happens, but the caller is too early in the protocol or a demo
	// cannot identify the correct server count
	// return the same count that demo will use
	return gHostSpawnCount;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Host_PostInit()
{
	if ( serverGameDLL )
	{
		serverGameDLL->PostInit();
	}

#if !defined( SWDS )
	if ( g_ClientDLL )
	{
		g_ClientDLL->PostInit();
	}

	toolframework->PostInit();

	if ( !sv.IsDedicated() )
	{
		// vgui needs other systems to finalize
		EngineVGui()->PostInit();
	}

#if defined( LINUX ) && !defined ANDROID
	const char en_US[] = "en_US.UTF-8";
	const char *CurrentLocale = setlocale( LC_ALL, NULL );
	if ( !CurrentLocale )
		CurrentLocale = "c";
	if ( Q_stricmp( CurrentLocale, en_US ) )
	{
		char MessageText[ 512 ];

		V_sprintf_safe( MessageText, "SetLocale('%s') failed. Using '%s'.\n"
									 "You may have limited glyph support.\n"
									 "Please install '%s' locale.",
						en_US, CurrentLocale, en_US );
		SDL_ShowSimpleMessageBox( 0, "Warning", MessageText, GetAssertDialogParent() );
	}
#endif // LINUX

#endif
}

void HLTV_Init()
{
	Assert ( hltv == NULL );
	Assert ( hltvtest == NULL );
}

void HLTV_Shutdown()
{
	if ( hltv )
	{
		hltv->Shutdown();
		delete hltv;
		hltv = NULL;
	}

	if ( hltvtest )
	{
		delete hltvtest;
		hltvtest = NULL;
	}
}

// Check with steam to see if the requested file (requires full path) is a valid, signed binary
bool DLL_LOCAL Host_IsValidSignature( const char *pFilename, bool bAllowUnknown )
{
#if defined( SWDS ) || defined(_X360)
	return true;
#else
	if ( sv.IsDedicated() || IsOSX() || IsLinux() || IsBSD() )
	{
		// dedicated servers and Mac and Linux  binaries don't check signatures
		return true;
	}
	else
	{
		if ( Steam3Client().SteamUtils() )
		{
			SteamAPICall_t hAPICall = Steam3Client().SteamUtils()->CheckFileSignature( pFilename );
			bool bAPICallFailed = true;
			while ( !Steam3Client().SteamUtils()->IsAPICallCompleted(hAPICall, &bAPICallFailed) )
			{
				SteamAPI_RunCallbacks();
				ThreadSleep( 1 );
			}

			if( bAPICallFailed )
			{
				Warning( "CheckFileSignature API call on %s failed", pFilename );
			}
			else
			{
				CheckFileSignature_t result;
				Steam3Client().SteamUtils()->GetAPICallResult( hAPICall, &result, sizeof(result), result.k_iCallback, &bAPICallFailed );
				if( bAPICallFailed )
				{
					Warning( "CheckFileSignature API call on %s failed\n", pFilename );
				}
				else
				{
					if( result.m_eCheckFileSignature == k_ECheckFileSignatureValidSignature || result.m_eCheckFileSignature == k_ECheckFileSignatureNoSignaturesFoundForThisApp )
						return true;
					if ( bAllowUnknown && result.m_eCheckFileSignature == k_ECheckFileSignatureNoSignaturesFoundForThisFile )
						return true;
					Warning( "No valid signature found for %s\n", pFilename );
				}
			}
		}
	}

	return false;
#endif // SWDS
}


// Ask steam if it is ok to load this DLL.  Unsigned DLLs should not be loaded unless
// the client is running -insecure (testing a plugin for example)
// This keeps legitimate users with modified binaries from getting VAC banned because of them
bool DLL_LOCAL Host_AllowLoadModule( const char *pFilename, const char *pPathID, bool bAllowUnknown, bool bIsServerOnly /* = false */ )
{
#if defined( SWDS ) || defined ( OSX ) || defined( LINUX )
	// dedicated servers and Mac and Linux binaries don't check signatures
	return true;
#else

	if ( sv.IsDedicated() || bIsServerOnly )
	{
		// dedicated servers and Mac binaries don't check signatures
		return true;
	}
	else
	{
		// check signature
		bool bSignatureIsValid = false;

		// Do we need to do the signature checking? If secure servers are disabled, just skip it.
		if ( Host_IsSecureServerAllowed() )
		{
			if ( Steam3Client().SteamUtils() )
			{
				char szDllname[512];

				V_strncpy( szDllname, pFilename, sizeof(szDllname) );
				V_SetExtension( szDllname, g_pModuleExtension, sizeof(szDllname) );
				if ( pPathID )
				{
					char szFullPath[ 512 ];
					const char *pFullPath = g_pFileSystem->RelativePathToFullPath( szDllname, pPathID, szFullPath, sizeof(szFullPath) );
					if ( !pFullPath )
					{
						Warning("Can't find %s on disk\n", szDllname );
						bSignatureIsValid = false;
					}
					else
					{
						bSignatureIsValid = Host_IsValidSignature( pFullPath, bAllowUnknown );
					}
				}
				else
				{
					bSignatureIsValid = Host_IsValidSignature( szDllname, bAllowUnknown );
				}
			}
			else
			{
				Warning("Steam is not active, running in -insecure mode.\n");
				Host_DisallowSecureServers();
			}
		}
		else
		{
			Warning("Loading unsigned module %s\nAccess to secure servers is disabled.\n", pFilename );
			return true;
		}

		return bSignatureIsValid;
	}
#endif // SWDS
}

bool DLL_LOCAL Host_IsSecureServerAllowed()
{
	if ( CommandLine()->FindParm( "-insecure" ) || CommandLine()->FindParm( "-textmode" ) )
		g_bAllowSecureServers = false;

	return g_bAllowSecureServers;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Host_Init( bool bDedicated )
{
	realtime = 0;
	host_idealtime = 0;

#if defined(_WIN32)
	if ( CommandLine()->FindParm( "-pme" ) )
	{
		s_bInitPME = true;
	}
#endif

	if ( Host_IsSecureServerAllowed() )
	{
		// double check the engine's signature in case it was hooked/modified
		if ( !Host_AllowLoadModule( "engine" DLL_EXT_STRING, "EXECUTABLE_PATH", false, bDedicated ) )
		{
			// not supposed to load this but we will anyway
			Host_DisallowSecureServers();
		}
	}

	ThreadPoolStartParams_t startParams;
	if ( IsX360() )
	{
		// 360 overrides defaults, 2 computation threads distributed to core 1 and 2
		startParams.nThreads = 2;
		startParams.nStackSize = 256*1024;
		startParams.fDistribute = TRS_TRUE;
		startParams.bUseAffinityTable = true;
		startParams.iAffinityTable[0] = XBOX_PROCESSOR_2;
		startParams.iAffinityTable[1] = XBOX_PROCESSOR_4;
		ThreadSetAffinity( NULL, 1 );
	}
	if ( g_pThreadPool )
		g_pThreadPool->Start( startParams, "CmpJob" );

	// From const.h, the loaded game .dll will give us the correct value which is transmitted to the client
	host_state.interval_per_tick = DEFAULT_TICK_INTERVAL;

	InstallBitBufErrorHandler();

	TRACEINIT( Memory_Init(), Memory_Shutdown() );

	TRACEINIT( Con_Init(), Con_Shutdown() );

	TRACEINIT( Cbuf_Init(), Cbuf_Shutdown() );

	TRACEINIT( Cmd_Init(), Cmd_Shutdown() );	

	TRACEINIT( g_pCVar->Init(), g_pCVar->Shutdown() ); // So we can list cvars with "cvarlst"

#ifndef SWDS
	TRACEINIT( V_Init(), V_Shutdown() );
#endif

	TRACEINIT( COM_Init(), COM_Shutdown() );


#ifndef SWDS
	TRACEINIT( saverestore->Init(), saverestore->Shutdown() );
#endif

	TRACEINIT( Filter_Init(), Filter_Shutdown() );

#ifndef SWDS
	TRACEINIT( Key_Init(), Key_Shutdown() );
#endif

	// Check for special -dev flag
	if ( CommandLine()->FindParm( "-dev" ) || ( CommandLine()->FindParm( "-allowdebug" ) && !CommandLine()->FindParm( "-nodev" ) ) )
	{
		sv_cheats.SetValue( 1 );
		developer.SetValue( 1 );
	}
#ifdef _DEBUG
	developer.SetValue( 1 );
#endif

	// Should have read info from steam.inf by now.
	Assert( GetSteamInfIDVersionInfo().AppID != k_uAppIdInvalid );

	if ( CommandLine()->FindParm( "-nocrashdialog" ) )
	{
		// stop the various windows error message boxes from showing up (used by the auto-builder so it doesn't block on error) 
		Sys_NoCrashDialog();
	}

	TRACEINIT( NET_Init( bDedicated ), NET_Shutdown() );     

	TRACEINIT( g_GameEventManager.Init(), g_GameEventManager.Shutdown() );

	TRACEINIT( sv.Init( bDedicated ), sv.Shutdown() );

#if defined( REPLAY_ENABLED )
	if ( Replay_IsSupportedModAndPlatform() )
	{
		TRACEINIT( ReplaySystem_Init( bDedicated ), ReplaySystem_Shutdown() );
	}
#endif

	if ( !CommandLine()->FindParm( "-nogamedll" ) )
	{
		SV_InitGameDLL();
	}

	TRACEINIT( g_Log.Init(), g_Log.Shutdown() );

	TRACEINIT( HLTV_Init(), HLTV_Shutdown() );

	ConDMsg( "Heap: %5.2f Mb\n", host_parms.memsize/(1024.0f*1024.0f) );

#if !defined( SWDS )
	if ( !bDedicated )
	{
		TRACEINIT( CL_Init(), CL_Shutdown() );

		// NOTE: This depends on the mod search path being set up
		TRACEINIT( InitMaterialSystem(), ShutdownMaterialSystem() );

		TRACEINIT( modelloader->Init(), modelloader->Shutdown() );

		TRACEINIT( StaticPropMgr()->Init(), StaticPropMgr()->Shutdown() );

		TRACEINIT( InitStudioRender(), ShutdownStudioRender() );

		//startup vgui
		TRACEINIT( EngineVGui()->Init(), EngineVGui()->Shutdown() );

		TRACEINIT( TextMessageInit(), TextMessageShutdown() );

		TRACEINIT( ClientDLL_Init(), ClientDLL_Shutdown() );

		TRACEINIT( SCR_Init(), SCR_Shutdown() );

		TRACEINIT( R_Init(), R_Shutdown() ); 

		TRACEINIT( Decal_Init(), Decal_Shutdown() );

		// hookup interfaces
		EngineVGui()->Connect();
	}
	else
#endif
	{
		TRACEINIT( InitMaterialSystem(), ShutdownMaterialSystem() );

		TRACEINIT( modelloader->Init(), modelloader->Shutdown() );

		TRACEINIT( StaticPropMgr()->Init(), StaticPropMgr()->Shutdown() );

		TRACEINIT( InitStudioRender(), ShutdownStudioRender() );

		TRACEINIT( Decal_Init(), Decal_Shutdown() );

		cl.m_nSignonState = SIGNONSTATE_NONE; // disable client
	}

#ifndef SWDS
	Host_ReadConfiguration();
	TRACEINIT( S_Init(), S_Shutdown() );
#endif

	// Execute valve.rc
	Cbuf_AddText( "exec valve.rc\n" );

#if defined( REPLAY_ENABLED )
	// Execute replay.cfg if this is TF and they want to use the replay system
	if ( Replay_IsSupportedModAndPlatform() && CommandLine()->CheckParm( "-replay" ) )
	{
		const char *pConfigName = CommandLine()->ParmValue( "-replay", "replay.cfg" );
		Cbuf_AddText( va( "exec %s\n", pConfigName ) );
	}
#endif

	// Execute mod-specfic settings, without falling back based on search path.
	// This lets us set overrides for games while letting mods of those games
	// use the default settings.
	if ( g_pFileSystem->FileExists( "//mod/cfg/modsettings.cfg" ) )
	{
		Cbuf_AddText( "exec modsettings.cfg mod\n" );
	}

	// Mark DLL as active
	//	eng->SetNextState( InEditMode() ? IEngine::DLL_PAUSED : IEngine::DLL_ACTIVE );

	// Deal with Gore Settings
	Host_CheckGore();

	TelemetryTick();

	// Initialize processor subsystem, and print relevant information:
	Host_InitProcessor();

	// Mark hunklevel at end of startup
	Hunk_AllocName( 0, "-HOST_HUNKLEVEL-" );
	host_hunklevel = Hunk_LowMark();

#ifdef SOURCE_MT
	if ( CommandLine()->FindParm( "-swapcores" ) )
	{
		g_nMaterialSystemThread = 1;
		g_nServerThread = 0;
	}
#endif

	Host_AllowQueuedMaterialSystem( false );

	// Finished initializing
	host_initialized = true;

	host_checkheap = CommandLine()->FindParm( "-heapcheck" ) ? true : false;

	if ( host_checkheap )
	{
#if defined( _WIN32 )
		if ( _heapchk() != _HEAPOK )
		{
			Sys_Error( "Host_Init:  _heapchk() != _HEAPOK\n" );
		}
#endif
	}

	// go directly to run state with no active game
	HostState_Init();

	// check for reslist generation
	if ( CommandLine()->FindParm( "-makereslists" ) )
	{
		MapReslistGenerator().StartReslistGeneration();
	}

	// check for devshot generation
	if ( CommandLine()->FindParm( "-makedevshots" ) )
	{
		DevShotGenerator().StartDevShotGeneration();
	}

	// if running outside of steam and NOT a dedicated server then phone home (or if "-phonehome" is passed on the command line)
	if ( !sv.IsDedicated() || CommandLine()->FindParm( "-phonehome" ) )
	{
		// In debug, only run this check if -phonehome is on the command line (so a debug build will "just work").
		if ( IsDebug() && CommandLine()->FindParm( "-phonehome" ) )
		{
			phonehome->Init();
			phonehome->Message( IPhoneHome::PHONE_MSG_ENGINESTART, NULL );
		}
	}

#ifndef SWDS
	// Rebuild audio caches
	if ( !sv.IsDedicated() && S_IsInitted() )
	{
		if ( !MapReslistGenerator().IsEnabled() )
		{
			// only build caches if we aren't' generating reslists (you need reslists to make the caches)
			extern void CheckCacheBuild();
			CheckCacheBuild();
		}
	}
#endif

	Host_PostInit();
	EndLoadingUpdates( );
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	pRenderContext->SetNonInteractiveTempFullscreenBuffer( NULL, MATERIAL_NON_INTERACTIVE_MODE_STARTUP );
	pRenderContext->SetNonInteractivePacifierTexture( NULL, 0, 0, 0 );
}

//-----------------------------------------------------------------------------
// Adds hints to the loader to keep resources that are in the transition volume,
// as they may not be part of the next map's reslist.
//-----------------------------------------------------------------------------
void AddTransitionResources( CSaveRestoreData *pSaveData, const char *pLevelName, const char *pLandmarkName )
{
	if ( !IsX360() || ( g_pFileSystem->GetDVDMode() != DVDMODE_STRICT ) )
	{
		return;
	}

	// get the bit marked for the next level
	int transitionMask = 0;
	for ( int i = 0; i < pSaveData->levelInfo.connectionCount; i++ )
	{
		if ( !Q_stricmp( pLevelName, pSaveData->levelInfo.levelList[i].mapName ) && !Q_stricmp( pLandmarkName, pSaveData->levelInfo.levelList[i].landmarkName ) )
		{
			transitionMask = 1<<i;
			break;
		}
	}
	
	if ( !transitionMask )
	{
		// nothing to do
		return;
	}

	const char *pModelName;
	bool bHasHumans = false;
	for ( int i = 0; i < pSaveData->NumEntities(); i++ )
	{
		if ( pSaveData->GetEntityInfo(i)->flags & transitionMask )
		{
			// this entity will cross the transition and needs to be preserved
			// add to the next map's resource list which effectively keeps it from being purged
			// only care about the actual mdl and not any of its dependants
			pModelName = pSaveData->GetEntityInfo(i)->modelname.ToCStr();
			g_pQueuedLoader->AddMapResource( pModelName );

			// humans require a post pass
			if ( !bHasHumans && V_stristr( pModelName, "models/humans" ) )
			{
				bHasHumans = true;
			}
		}
	}

	if ( bHasHumans )
	{
		// the presence of any human entity in the transition needs to ensure all the human mdls stay
		int count = modelloader->GetCount();
		for ( int i = 0; i < count; i++ )
		{
			pModelName = modelloader->GetName( modelloader->GetModelForIndex( i ) );
			if ( V_stristr( pModelName, "models/humans" ) )
			{
				g_pQueuedLoader->AddMapResource( pModelName );
			}
		}
	}
}

bool Host_Changelevel( bool loadfromsavedgame, const char *mapname, const char *start )
{
	char			_startspot[MAX_QPATH];
	char			*startspot;
	char			oldlevel[MAX_PATH];
#if !defined(SWDS)
	CSaveRestoreData *pSaveData = NULL;
#endif
	bool bTransitionBySave = false;

	if ( !sv.IsActive() )
	{
		ConMsg("Only the server may changelevel\n");
		return false;
	}

#ifndef SWDS
	// FIXME:  Even needed?
	if ( demoplayer->IsPlayingBack() )
	{
		ConMsg("Changelevel invalid during demo playback\n");
		SCR_EndLoadingPlaque();
		return false;
	}

#endif

#ifndef SWDS
	SCR_BeginLoadingPlaque();

	// stop sounds (especially looping!)
	S_StopAllSounds(true);
#endif

	// Prepare new level
	sv.InactivateClients();

	// The qualified name of the map, excluding path/extension
	char szMapName[MAX_PATH] = { 0 };
	// The file to load the map from.
	char szMapFile[MAX_PATH] = { 0 };
	Q_strncpy( szMapName, mapname, sizeof( szMapName ) );
	Host_DefaultMapFileName( szMapName, szMapFile, sizeof( szMapFile ) );

	// Ask serverDLL to prepare this load
	if ( g_iServerGameDLLVersion >= 10 )
	{
		serverGameDLL->PrepareLevelResources( szMapName, sizeof( szMapName ), szMapFile, sizeof( szMapFile ) );
	}

	if ( !modelloader->Map_IsValid( szMapFile ) )
	{
#ifndef SWDS
		SCR_EndLoadingPlaque();
#endif
		// We have already inactivated clients at this point due to PrepareLevelResources being blocking, false alarm,
		// tell them to reconnect (which doesn't mean full reconnect, just start rejoining the map)
		//
		// In the likely case that the game DLL tries another map this is harmless, they'll wait on the game server in
		// the connect process if its in another level change by time they get there.
		sv.ReconnectClients();
		return false;
	}

	// If changing from the same map to the same map, optimize by not closing and reopening
	// the packfile which is embedded in the .bsp; we do this by incrementing the packfile's
	// refcount via BeginMapAccess()/EndMapAccess() through the base filesystem API.
	struct LocalMapAccessScope
	{
		LocalMapAccessScope() : bEnabled( false ) { }
		~LocalMapAccessScope() { if ( bEnabled ) g_pFileSystem->EndMapAccess(); }
		bool bEnabled;
	};

	LocalMapAccessScope mapscope;
	if ( V_strcmp( sv.GetMapName(), szMapName ) == 0 )
	{
		g_pFileSystem->BeginMapAccess();
		mapscope.bEnabled = true;
	}

	g_pFileSystem->AsyncFinishAll();

	if ( !start )
		startspot = NULL;
	else
	{
		Q_strncpy (_startspot, start, sizeof( _startspot ) );
		startspot = _startspot;
	}

	Warning( "---- Host_Changelevel ----\n" );
	CheckForFlushMemory( sv.GetMapName(), szMapName );

#if !defined( SWDS )
	// Always save as an xsave if we're on the X360
	saverestore->SetIsXSave( IsX360() );

	// Add on time passed since the last time we kept track till this transition
	int iAdditionalSeconds = g_ServerGlobalVariables.curtime - saverestore->GetMostRecentElapsedTimeSet();
	int iElapsedSeconds = saverestore->GetMostRecentElapsedSeconds() + iAdditionalSeconds;
	int iElapsedMinutes = saverestore->GetMostRecentElapsedMinutes() + ( iElapsedSeconds / 60 );
	saverestore->SetMostRecentElapsedMinutes( iElapsedMinutes );
	saverestore->SetMostRecentElapsedSeconds( ( iElapsedSeconds % 60 ) );

	if ( bTransitionBySave )
	{
		char comment[80];
		// Pass in the total elapsed time so it gets added to the elapsed time for this map.
		serverGameDLL->GetSaveComment( 
			comment, 
			sizeof( comment ),
			saverestore->GetMostRecentElapsedMinutes(),
			saverestore->GetMostRecentElapsedSeconds() );

		if ( !saverestore->SaveGameSlot( "_transition", comment, false, true, szMapName, startspot ) )
		{
			Warning( "Failed to save data for transition\n" );
			SCR_EndLoadingPlaque();
			return false;
		}

		// Not going to load a save after the transition, so add this map's elapsed time to the total elapsed time
		int totalSeconds = g_ServerGlobalVariables.curtime + saverestore->GetMostRecentElapsedSeconds();
		saverestore->SetMostRecentElapsedMinutes( (int)( totalSeconds / 60.0f ) + saverestore->GetMostRecentElapsedMinutes() );
		saverestore->SetMostRecentElapsedSeconds( (int)fmod( totalSeconds, 60.0f ) );
	}
#endif

	Q_strncpy( oldlevel, sv.GetMapName(), sizeof( oldlevel ) );

#if !defined(SWDS)
	if ( loadfromsavedgame )
	{
		if ( !bTransitionBySave )
		{
			// save the current level's state
			saverestore->SaveGameState( true, &pSaveData );

			if ( !pSaveData )
			{
				Warning( "Failed to save data for transition\n" );
				SCR_EndLoadingPlaque();
				return false;
			}
		}

		// ensure resources in the transition volume stay
		AddTransitionResources( pSaveData, szMapName, startspot );
	}
#endif
	g_pServerPluginHandler->LevelShutdown();

#if !defined(SWDS)
	audiosourcecache->LevelShutdown();
#endif

#if !defined(SWDS)
	saverestore->FinishAsyncSave();
#endif

	if ( sv.RestartOnLevelChange() )
	{
		Cbuf_Clear();
		Cbuf_AddText( "quit\n" );
		return false;
	}

	DownloadListGenerator().OnLevelLoadStart( szMapName );

	if ( !sv.SpawnServer( szMapName, szMapFile, startspot ) )
	{
#ifndef SWDS
		SCR_EndLoadingPlaque();
#endif
		return false;
	}

#ifndef SWDS
	if ( loadfromsavedgame )
	{
		if ( !bTransitionBySave )
		{
			// Finish saving gamestate
			saverestore->Finish( pSaveData );
		}

		g_ServerGlobalVariables.curtime = sv.GetTime();

		audiosourcecache->LevelInit( szMapName );
		g_pServerPluginHandler->LevelInit( szMapName, CM_EntityString(), oldlevel, startspot, true, false );

		sv.SetPaused( true ); // pause until client connects
		sv.m_bLoadgame = true;
	}
	else
#endif
	{
		g_ServerGlobalVariables.curtime = sv.GetTime();
#if !defined(SWDS)
		audiosourcecache->LevelInit( szMapName );
#endif
		g_pServerPluginHandler->LevelInit( szMapName, CM_EntityString(), NULL, NULL, false, false );
	}

	SV_ActivateServer();

#if !defined(SWDS)
	// Offset stored elapsed time by the current elapsed time for this new map
	int maptime = sv.GetTime();
	int minutes = (int)( maptime / 60.0f );
	int seconds = (int)fmod( maptime, 60.0f );
	saverestore->SetMostRecentElapsedMinutes( saverestore->GetMostRecentElapsedMinutes() - minutes );
	saverestore->SetMostRecentElapsedSeconds( saverestore->GetMostRecentElapsedSeconds() - seconds );
#endif

	NotifyDedicatedServerUI("UpdateMap");

	DownloadListGenerator().OnLevelLoadEnd();

	return true;
}

/*
===============================================================================

SERVER TRANSITIONS

===============================================================================
*/
bool Host_NewGame( char *mapName, bool loadGame, bool bBackgroundLevel, const char *pszOldMap, const char *pszLandmark, bool bOldSave )
{
	VPROF( "Host_NewGame" );
	COM_TimestampedLog( "Host_NewGame" );

	char previousMapName[MAX_PATH] = { 0 };
	Q_strncpy( previousMapName, host_map.GetString(), sizeof( previousMapName ) );

#ifndef SWDS
	SCR_BeginLoadingPlaque();
#endif

	// The qualified name of the map, excluding path/extension
	char szMapName[MAX_PATH] = { 0 };
	// The file to load the map from.
	char szMapFile[MAX_PATH] = { 0 };
	Q_strncpy( szMapName, mapName, sizeof( szMapName ) );
	Host_DefaultMapFileName( szMapName, szMapFile, sizeof( szMapFile ) );

	// Steam may not have been started yet, ensure it is available to the game DLL before we ask it to prepare level
	// resources
	SV_InitGameServerSteam();

	// Ask serverDLL to prepare this load
	if ( g_iServerGameDLLVersion >= 10 )
	{
		serverGameDLL->PrepareLevelResources( szMapName, sizeof( szMapName ), szMapFile, sizeof( szMapFile ) );
	}

	if ( !modelloader->Map_IsValid( szMapFile ) )
	{
#ifndef SWDS
		SCR_EndLoadingPlaque();
#endif
		return false;
	}

	DevMsg( "---- Host_NewGame ----\n" );
	host_map.SetValue( szMapName );

	CheckForFlushMemory( previousMapName, szMapName );

	if (MapReslistGenerator().IsEnabled())
	{
		// uncache all the materials, so their files get referenced again for the reslists
		// undone for now, since we're just trying to get a global reslist, not per-map accurate
		//		materials->UncacheAllMaterials();
		MapReslistGenerator().OnLevelLoadStart(szMapName);
		// cache 'em back in!
		//		materials->CacheUsedMaterials();
	}
	DownloadListGenerator().OnLevelLoadStart(szMapName);

	if ( !loadGame )
	{
		VPROF( "Host_NewGame_HostState_RunGameInit" );
		HostState_RunGameInit();
	}

	// init network mode
	VPROF_SCOPE_BEGIN( "Host_NewGame_SpawnServer" );

	NET_SetMutiplayer( sv.IsMultiplayer() );

	NET_ListenSocket( sv.m_Socket, true );	// activated server TCP socket

	// let's not have any servers with no name
	if ( host_name.GetString()[0] == 0 )
	{
		host_name.SetValue( serverGameDLL->GetGameDescription() );
	}

	if ( !sv.SpawnServer ( szMapName, szMapFile, NULL ) )
	{
		return false;
	}

	sv.m_bIsLevelMainMenuBackground = bBackgroundLevel;

	VPROF_SCOPE_END();

	// make sure the time is set
	g_ServerGlobalVariables.curtime = sv.GetTime();

	COM_TimestampedLog( "serverGameDLL->LevelInit" );

#ifndef SWDS
	EngineVGui()->UpdateProgressBar(PROGRESS_LEVELINIT);

	audiosourcecache->LevelInit( szMapName );
#endif

	g_pServerPluginHandler->LevelInit( szMapName, CM_EntityString(), pszOldMap, pszLandmark, loadGame && !bOldSave, bBackgroundLevel );

	if ( loadGame && !bOldSave )
	{
		sv.SetPaused( true );		// pause until all clients connect
		sv.m_bLoadgame = true;
		g_ServerGlobalVariables.curtime = sv.GetTime();
	}

	if( !SV_ActivateServer() )
	{
		return false;
	}

	// Connect the local client when a "map" command is issued.
	if ( !sv.IsDedicated() )
	{
		COM_TimestampedLog( "Stuff 'connect localhost' to console" );

		char str[512];
		Q_snprintf( str, sizeof( str ), "connect localhost:%d listenserver", sv.GetUDPPort() );
		Cbuf_AddText( str );
	}
	else
	{
		// Dedicated server triggers map load here.
		GetTestScriptMgr()->CheckPoint( "FinishedMapLoad" );
	}

#ifndef SWDS
	if ( !loadGame || bOldSave )
	{
		// clear the most recent remember save, so the level will just restart if the player dies
		saverestore->ForgetRecentSave();
	}

	saverestore->SetMostRecentElapsedMinutes( 0 );
	saverestore->SetMostRecentElapsedSeconds( 0 );
#endif

	if (MapReslistGenerator().IsEnabled())
	{
		MapReslistGenerator().OnLevelLoadEnd();
	}
	DownloadListGenerator().OnLevelLoadEnd();
	return true;
}

void Host_FreeStateAndWorld( bool server )
{
	bool bNeedsPurge = false;

	Assert( host_initialized );
	Assert( host_hunklevel );

	// If called by the client and we are running a listen server, just ignore
	if ( !server && sv.IsActive() )
		return;

	// HACKHACK: You can't clear the hunk unless the client data is free
	// since this gets called by the server, it's necessary to wipe the client
	// in case we are on a listen server
#ifndef SWDS
	if ( server && !sv.IsDedicated() )
	{
		CL_ClearState();
	}
#endif

	// The world model relies on the low hunk, so we need to force it to unload
	if ( host_state.worldmodel )
	{
		modelloader->UnreferenceModel( host_state.worldmodel, IModelLoader::FMODELLOADER_SERVER );
		modelloader->UnreferenceModel( host_state.worldmodel, IModelLoader::FMODELLOADER_CLIENT );
		host_state.SetWorldModel( NULL );
		bNeedsPurge = server && true;
	}

	// Unload and reset dynamic models
	if ( server )
	{
		extern IVModelInfo* modelinfo;
		modelinfo->OnLevelChange();
	}
#ifndef SWDS
	else
	{
		extern IVModelInfoClient* modelinfoclient;
		modelinfoclient->OnLevelChange();
	}
#endif

	modelloader->UnloadUnreferencedModels();

	g_TimeLastMemTest = 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Host_FreeToLowMark( bool server )
{
	Assert( host_initialized );
	Assert( host_hunklevel );

	// If called by the client and we are running a listen server, just ignore
	if ( !server && sv.IsActive() )
		return;

	CM_FreeMap();

	if ( host_hunklevel )
	{
		// See if we are going to obliterate any malloc'd pointers
		Hunk_FreeToLowMark(host_hunklevel);
	}
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Host_Shutdown(void)
{
#ifndef ANDROID
	extern void ShutdownMixerControls();
#endif

	if ( host_checkheap )
	{
#ifdef _WIN32
		if ( _heapchk() != _HEAPOK )
		{
			Sys_Error( "Host_Shutdown (top):  _heapchk() != _HEAPOK\n" );
		}
#endif
	}

	// Check for recursive shutdown, should never happen
	static bool shutting_down = false;
	if ( shutting_down )
	{
		Msg( "Recursive shutdown!!!\n" );
		return;
	}
	shutting_down = true;

	phonehome->Message( IPhoneHome::PHONE_MSG_ENGINEEND, NULL );
	phonehome->Shutdown();

#ifndef SWDS
	// Store active configuration settings
	Host_WriteConfiguration(); 
#endif

	// Disconnect from server
	Host_Disconnect(true);

#ifndef SWDS
	// keep ConMsg from trying to update the screen
	scr_disabled_for_loading = true;
#endif

#if defined VOICE_OVER_IP && !defined SWDS && !defined( NO_VOICE ) //!defined(_XBOX)
	Voice_Deinit();
#endif // VOICE_OVER_IP

	// TODO, Trace this
	CM_FreeMap();

	host_initialized = false;

#if defined(VPROF_ENABLED)
	VProfRecord_Shutdown();
#endif

#if !defined SWDS
	if ( !sv.IsDedicated() )
	{
		TRACESHUTDOWN( Decal_Shutdown() );

		TRACESHUTDOWN( R_Shutdown() );

		TRACESHUTDOWN( SCR_Shutdown() );

		TRACESHUTDOWN( S_Shutdown() );

		TRACESHUTDOWN( ClientDLL_Shutdown() );

		TRACESHUTDOWN( TextMessageShutdown() );

		TRACESHUTDOWN( EngineVGui()->Shutdown() );

		TRACESHUTDOWN( StaticPropMgr()->Shutdown() );

		// Model loader must shutdown before StudioRender
		// because it calls into StudioRender
		TRACESHUTDOWN( modelloader->Shutdown() );

		TRACESHUTDOWN( ShutdownStudioRender() );

		TRACESHUTDOWN( ShutdownMaterialSystem() );

		TRACESHUTDOWN( CL_Shutdown() );
	}
	else
#endif
	{
		TRACESHUTDOWN( Decal_Shutdown() );

		TRACESHUTDOWN( modelloader->Shutdown() );

		TRACESHUTDOWN( ShutdownStudioRender() );

		TRACESHUTDOWN( StaticPropMgr()->Shutdown() );

		TRACESHUTDOWN( ShutdownMaterialSystem() );
	}

#if defined( REPLAY_ENABLED )
	if ( Replay_IsSupportedModAndPlatform() )
	{
		TRACESHUTDOWN( ReplaySystem_Shutdown() );
	}
#endif

	TRACESHUTDOWN( HLTV_Shutdown() );

	TRACESHUTDOWN( g_Log.Shutdown() );

	TRACESHUTDOWN( g_GameEventManager.Shutdown() );

	TRACESHUTDOWN( sv.Shutdown() );

	TRACESHUTDOWN( NET_Shutdown() );

#ifndef SWDS
	TRACESHUTDOWN( Key_Shutdown() );
#if !defined _X360 && !defined ANDROID
	TRACESHUTDOWN( ShutdownMixerControls() );
#endif
#endif

	TRACESHUTDOWN( Filter_Shutdown() );

#ifndef SWDS
	TRACESHUTDOWN( saverestore->Shutdown() );
#endif

	TRACESHUTDOWN( COM_Shutdown() );

	// TRACESHUTDOWN( Host_ShutdownVCR() );
#ifndef SWDS
	TRACESHUTDOWN( V_Shutdown() );
#endif

	TRACESHUTDOWN( g_pCVar->Shutdown() );

	TRACESHUTDOWN( Cmd_Shutdown() );

	TRACESHUTDOWN( Cbuf_Shutdown() );

	TRACESHUTDOWN( Con_Shutdown() );

	TRACESHUTDOWN( Memory_Shutdown() );

	if ( g_pThreadPool )
		g_pThreadPool->Stop();

	DTI_Term();
	ServerDTI_Term();

#if defined(_WIN32)
	if ( s_bInitPME )
	{
		ShutdownPME();
	}
#endif

	if ( host_checkheap )
	{
#ifdef _WIN32
		if ( _heapchk() != _HEAPOK )
		{
			Sys_Error( "Host_Shutdown (bottom):  _heapchk() != _HEAPOK\n" );
		}
#endif
	}
}

//-----------------------------------------------------------------------------
// Centralize access to enabling QMS.
//-----------------------------------------------------------------------------
bool Host_AllowQueuedMaterialSystem( bool bAllow )
{
#if !defined DEDICATED
	// g_bAllowThreadedSound = bAllow;
	// NOTE: Moved this to materialsystem for integrating with other mqm changes
	return g_pMaterialSystem->AllowThreading( bAllow, g_nMaterialSystemThread );
#endif
	return false;
}