//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// Defines the entry point for the application.
//
//===========================================================================//

#if defined( _WIN32 ) && !defined( _X360 )
#include <windows.h>
#include "shlwapi.h" // registry stuff
#include <direct.h>
#elif defined ( LINUX ) || defined( OSX )
	#define O_EXLOCK 0
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <locale.h>
#elif defined ( _X360 )
#else
#error
#endif
#include "appframework/ilaunchermgr.h"
#include <stdio.h>
#include "tier0/icommandline.h"
#include "engine_launcher_api.h"
#include "tier0/vcrmode.h"
#include "ifilesystem.h"
#include "tier1/interface.h"
#include "tier0/dbg.h"
#include "iregistry.h"
#include "appframework/IAppSystem.h"
#include "appframework/AppFramework.h"
#include <vgui/VGUI.h>
#include <vgui/ISurface.h>
#include "tier0/platform.h"
#include "tier0/memalloc.h"
#include "filesystem.h"
#include "tier1/utlrbtree.h"
#include "materialsystem/imaterialsystem.h"
#include "istudiorender.h"
#include "vgui/IVGui.h"
#include "IHammer.h"
#include "datacache/idatacache.h"
#include "datacache/imdlcache.h"
#include "vphysics_interface.h"
#include "filesystem_init.h"
#include "vstdlib/iprocessutils.h"
#include "video/ivideoservices.h"
#include "tier1/tier1.h"
#include "tier2/tier2.h"
#include "tier3/tier3.h"
#include "p4lib/ip4.h"
#include "inputsystem/iinputsystem.h"
#include "filesystem/IQueuedLoader.h"
#include "reslistgenerator.h"
#include "tier1/fmtstr.h"
#include "sourcevr/isourcevirtualreality.h"

#define VERSION_SAFE_STEAM_API_INTERFACES
#include "steam/steam_api.h"

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#include "xbox/xbox_console.h"
#include "xbox/xbox_launch.h"
#endif

#if defined( USE_SDL )
#include "SDL.h"

#if !defined( _WIN32 )
#define MB_OK 			0x00000001
#define MB_SYSTEMMODAL	0x00000002
#define MB_ICONERROR	0x00000004
int MessageBox( HWND hWnd, const char *message, const char *header, unsigned uType );
#endif // _WIN32

#endif // USE_SDL

#if defined( POSIX )
#define RELAUNCH_FILE "/tmp/hl2_relaunch"
#endif

#if defined ( ANDROID )
#include <android/log.h>
#include "jni.h"
#endif

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

#define DEFAULT_HL2_GAMEDIR	"hl2"

#if defined( USE_SDL )
extern void* CreateSDLMgr();
#endif

//-----------------------------------------------------------------------------
// Modules...
//-----------------------------------------------------------------------------
static IEngineAPI *g_pEngineAPI;
static IHammer *g_pHammer;

bool g_bTextMode = false;

static char g_szBasedir[MAX_PATH];
static char g_szGamedir[MAX_PATH];

// copied from sys.h
struct FileAssociationInfo
{
	char const  *extension;
	char const  *command_to_issue;
};

static FileAssociationInfo g_FileAssociations[] =
{
	{ ".dem", "playdemo" },
	{ ".sav", "load" },
	{ ".bsp", "map" },
};

#ifdef _WIN32
#pragma warning(disable:4073)
#pragma init_seg(lib)
#endif

class CLeakDump
{
public:
	CLeakDump()
	 :	m_bCheckLeaks( false )
	{
	}

	~CLeakDump()
	{
		if ( m_bCheckLeaks )
		{
			MemAlloc_DumpStats();
		}
	}

	bool m_bCheckLeaks;
} g_LeakDump;

//-----------------------------------------------------------------------------
// Spew function!
//-----------------------------------------------------------------------------
SpewRetval_t LauncherDefaultSpewFunc( SpewType_t spewType, char const *pMsg )
{
#ifndef _CERT
#ifdef WIN32
	OutputDebugStringA( pMsg );
#else
	fprintf( stderr, "%s", pMsg );
#endif
	
	switch( spewType )
	{
	case SPEW_MESSAGE:
	case SPEW_LOG:
		return SPEW_CONTINUE;

	case SPEW_WARNING:
		if ( !stricmp( GetSpewOutputGroup(), "init" ) )
		{
#if defined( WIN32 ) || defined( USE_SDL )
			::MessageBox( NULL, pMsg, "Warning!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
#endif
		}
		return SPEW_CONTINUE;

	case SPEW_ASSERT:
		if ( !ShouldUseNewAssertDialog() )
		{
#if defined( WIN32 ) || defined( USE_SDL )
			::MessageBox( NULL, pMsg, "Assert!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
#endif
		}
		return SPEW_DEBUGGER;
	
	case SPEW_ERROR:
	default:
#if defined( WIN32 ) || defined( USE_SDL )
		::MessageBox( NULL, pMsg, "Error!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
#endif
		_exit( 1 );
	}
#else
	if ( spewType != SPEW_ERROR)
		return SPEW_CONTINUE;
	_exit( 1 );
#endif
}


//-----------------------------------------------------------------------------
// Implementation of VCRHelpers.
//-----------------------------------------------------------------------------
class CVCRHelpers : public IVCRHelpers
{
public:
	virtual void ErrorMessage( const char *pMsg )
	{
#if defined( WIN32 ) || defined( LINUX )
		NOVCR( ::MessageBox( NULL, pMsg, "VCR Error", MB_OK ) );
#endif
	}

	virtual void* GetMainWindow()
	{
		return NULL;
	}
};

static CVCRHelpers g_VCRHelpers;

//-----------------------------------------------------------------------------
// Purpose: Return the game directory
// Output : char
//-----------------------------------------------------------------------------
char *GetGameDirectory( void )
{
	return g_szGamedir;
}

void SetGameDirectory( const char *game )
{
	Q_strncpy( g_szGamedir, game, sizeof(g_szGamedir) );
}

//-----------------------------------------------------------------------------
// Gets the executable name
//-----------------------------------------------------------------------------
bool GetExecutableName( char *out, int outSize )
{
#ifdef WIN32
	if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, outSize ) )
	{
		return false;
	}
	return true;
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Return the base directory
// Output : char
//-----------------------------------------------------------------------------
char *GetBaseDirectory( void )
{
#ifdef ANDROID
	return getenv("VALVE_GAME_PATH");
#else
	return g_szBasedir;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Determine the directory where this .exe is running from
//-----------------------------------------------------------------------------
void UTIL_ComputeBaseDir()
{
	g_szBasedir[0] = 0;

	if ( IsX360() )
	{
		char const *pBaseDir = CommandLine()->ParmValue( "-basedir" );
		if ( pBaseDir )
		{
			strcpy( g_szBasedir, pBaseDir );
		}
	}

	if ( !g_szBasedir[0] && GetExecutableName( g_szBasedir, sizeof( g_szBasedir ) ) )
	{
		char *pBuffer = strrchr( g_szBasedir, '\\' );
		if ( *pBuffer )
		{
			*(pBuffer+1) = '\0';
		}

		int j = strlen( g_szBasedir );
		if (j > 0)
		{
			if ( ( g_szBasedir[j-1] == '\\' ) || 
				 ( g_szBasedir[j-1] == '/' ) )
			{
				g_szBasedir[j-1] = 0;
			}
		}
	}

	if ( IsPC() )
	{
		char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" );
		if ( pOverrideDir )
		{
			strcpy( g_szBasedir, pOverrideDir );
		}
	}

#ifdef WIN32
	Q_strlower( g_szBasedir );
#endif
	Q_FixSlashes( g_szBasedir );
}

#ifdef WIN32
BOOL WINAPI MyHandlerRoutine( DWORD dwCtrlType )
{
#if !defined( _X360 )
	TerminateProcess( GetCurrentProcess(), 2 );
#endif
	return TRUE;
}
#endif

void InitTextMode()
{
#ifdef WIN32
#if !defined( _X360 )
	AllocConsole();

	SetConsoleCtrlHandler( MyHandlerRoutine, TRUE );

	freopen( "CONIN$", "rb", stdin );		// reopen stdin handle as console window input
	freopen( "CONOUT$", "wb", stdout );		// reopen stout handle as console window output
	freopen( "CONOUT$", "wb", stderr );		// reopen stderr handle as console window output
#else
	XBX_Error( "%s %s: Not Supported", __FILE__, __LINE__ );
#endif
#endif
}

void SortResList( char const *pchFileName, char const *pchSearchPath );

#define ALL_RESLIST_FILE	"all.lst"
#define ENGINE_RESLIST_FILE  "engine.lst"

// create file to dump out to
class CLogAllFiles
{
public:
	CLogAllFiles();
	void Init();
	void Shutdown();
	void LogFile( const char *fullPathFileName, const char *options );

private:
	static void LogAllFilesFunc( const char *fullPathFileName, const char *options );
	void LogToAllReslist( char const *line );

	bool		m_bActive;
	char		m_szCurrentDir[_MAX_PATH];

	// persistent across restarts
	CUtlRBTree< CUtlString, int > m_Logged;
	CUtlString	m_sResListDir;
	CUtlString	m_sFullGamePath;
};

static CLogAllFiles g_LogFiles;

static bool AllLogLessFunc( CUtlString const &pLHS, CUtlString const &pRHS )
{
	return CaselessStringLessThan( pLHS.Get(), pRHS.Get() );
}

CLogAllFiles::CLogAllFiles() :
	m_bActive( false ),
	m_Logged( 0, 0, AllLogLessFunc )
{
	MEM_ALLOC_CREDIT();
	m_sResListDir = "reslists";
}

void CLogAllFiles::Init()
{
	if ( IsX360() )
	{
		return;
	}

	// Can't do this in edit mode
	if ( CommandLine()->CheckParm( "-edit" ) )
	{
		return;
	}

	if ( !CommandLine()->CheckParm( "-makereslists" ) )
	{
		return;
	}

	m_bActive = true;

	char const *pszDir = NULL;
	if ( CommandLine()->CheckParm( "-reslistdir", &pszDir ) && pszDir )
	{
		char szDir[ MAX_PATH ];
		Q_strncpy( szDir, pszDir, sizeof( szDir ) );
		Q_StripTrailingSlash( szDir );
#ifdef WIN32
		Q_strlower( szDir );
#endif
		Q_FixSlashes( szDir );
		if ( Q_strlen( szDir ) > 0 )
		{
			m_sResListDir = szDir;
		}
	}

	// game directory has not been established yet, must derive ourselves
	char path[MAX_PATH];
	Q_snprintf( path, sizeof(path), "%s/%s", GetBaseDirectory(), CommandLine()->ParmValue( "-game", "hl2" ) );
	Q_FixSlashes( path );
#ifdef WIN32
	Q_strlower( path );
#endif
	m_sFullGamePath = path;

	// create file to dump out to
	char szDir[ MAX_PATH ];
	V_snprintf( szDir, sizeof( szDir ), "%s\\%s", m_sFullGamePath.String(), m_sResListDir.String() );
	g_pFullFileSystem->CreateDirHierarchy( szDir, "GAME" );

	g_pFullFileSystem->AddLoggingFunc( &LogAllFilesFunc );

	if ( !CommandLine()->FindParm( "-startmap" ) && !CommandLine()->FindParm( "-startstage" ) )
	{
		m_Logged.RemoveAll();
		g_pFullFileSystem->RemoveFile( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "GAME" );
	}

#ifdef WIN32
	::GetCurrentDirectory( sizeof(m_szCurrentDir), m_szCurrentDir );
	Q_strncat( m_szCurrentDir, "\\", sizeof(m_szCurrentDir), 1 );
	_strlwr( m_szCurrentDir );
#else
	getcwd( m_szCurrentDir, sizeof(m_szCurrentDir) );
	Q_strncat( m_szCurrentDir, "/", sizeof(m_szCurrentDir), 1 );
#endif
}

void CLogAllFiles::Shutdown()
{
	if ( !m_bActive )
		return;

	m_bActive = false;

	if ( CommandLine()->CheckParm( "-makereslists" ) )
	{
		g_pFullFileSystem->RemoveLoggingFunc( &LogAllFilesFunc );
	}

	// Now load and sort all.lst
	SortResList( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "GAME" );
	// Now load and sort engine.lst
	SortResList( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ENGINE_RESLIST_FILE ), "GAME" );

	m_Logged.Purge();
}

void CLogAllFiles::LogToAllReslist( char const *line )
{
	// Open for append, write data, close.
	FileHandle_t fh = g_pFullFileSystem->Open( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "at", "GAME" );
	if ( fh != FILESYSTEM_INVALID_HANDLE )
	{
		g_pFullFileSystem->Write("\"", 1, fh);
		g_pFullFileSystem->Write( line, Q_strlen(line), fh );
		g_pFullFileSystem->Write("\"\n", 2, fh);
		g_pFullFileSystem->Close( fh );
	}
}

void CLogAllFiles::LogFile(const char *fullPathFileName, const char *options)
{
	if ( !m_bActive )
	{
		Assert( 0 );
		return;
	}

	// write out to log file
	Assert( fullPathFileName[1] == ':' );

	int idx = m_Logged.Find( fullPathFileName );
	if ( idx != m_Logged.InvalidIndex() )
	{
		return;
	}

	m_Logged.Insert( fullPathFileName );

	// make it relative to our root directory
	const char *relative = Q_stristr( fullPathFileName, GetBaseDirectory() );
	if ( relative )
	{
		relative += ( Q_strlen( GetBaseDirectory() ) + 1 );

		char rel[ MAX_PATH ];
		Q_strncpy( rel, relative, sizeof( rel ) );
#ifdef WIN32
		Q_strlower( rel );
#endif
		Q_FixSlashes( rel );
		
		LogToAllReslist( rel );
	}
}

//-----------------------------------------------------------------------------
// Purpose: callback function from filesystem
//-----------------------------------------------------------------------------
void CLogAllFiles::LogAllFilesFunc(const char *fullPathFileName, const char *options)
{
	g_LogFiles.LogFile( fullPathFileName, options );
}

//-----------------------------------------------------------------------------
// Purpose: This is a bit of a hack because it appears 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
static bool IsWin98OrOlder()
{
	bool retval = false;

#if defined( WIN32 ) && !defined( _X360 )
	OSVERSIONINFOEX osvi;
	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	
	BOOL bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
	if( !bOsVersionInfoEx )
	{
		// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
		osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
		if ( !GetVersionEx ( (OSVERSIONINFO *) &osvi) )
		{
			Error( "IsWin98OrOlder:  Unable to get OS version information" );
		}
	}

	switch (osvi.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_NT:
		// NT, XP, Win2K, etc. all OK for SSE
		break;
	case VER_PLATFORM_WIN32_WINDOWS:
		// Win95, 98, Me can't do SSE
		retval = true;
		break;
	case VER_PLATFORM_WIN32s:
		// Can't really run this way I don't think...
		retval = true;
		break;
	default:
		break;
	}
#endif

	return retval;
}


//-----------------------------------------------------------------------------
// Purpose: Figure out if Steam is running, then load the GameOverlayRenderer.dll
//-----------------------------------------------------------------------------
void TryToLoadSteamOverlayDLL()
{
#if defined( WIN32 ) && !defined( _X360 )
	// First, check if the module is already loaded, perhaps because we were run from Steam directly
	HMODULE hMod = GetModuleHandle( "GameOverlayRenderer" DLL_EXT_STRING );
	if ( hMod )
	{
		return;
	}

	if ( 0 == GetEnvironmentVariableA( "SteamGameId", NULL, 0 ) )
	{
		// Initializing the Steam client API has the side effect of setting up the AppId
		// which is immediately queried in GameOverlayRenderer.dll's DllMain entry point
		if( SteamAPI_InitSafe() )
		{
			const char *pchSteamInstallPath = SteamAPI_GetSteamInstallPath();
			if ( pchSteamInstallPath )
			{
				char rgchSteamPath[MAX_PATH];
				V_ComposeFileName( pchSteamInstallPath, "GameOverlayRenderer" DLL_EXT_STRING, rgchSteamPath, Q_ARRAYSIZE(rgchSteamPath) );
				// This could fail, but we can't fix it if it does so just ignore failures
				LoadLibrary( rgchSteamPath );
			
			}

			SteamAPI_Shutdown();
		}
	}

#endif
}

//-----------------------------------------------------------------------------
// Inner loop: initialize, shutdown main systems, load steam to 
//-----------------------------------------------------------------------------
class CSourceAppSystemGroup : public CSteamAppSystemGroup
{
public:
	// Methods of IApplication
	virtual bool Create();
	virtual bool PreInit();
	virtual int Main();
	virtual void PostShutdown();
	virtual void Destroy();

private:
	const char *DetermineDefaultMod();
	const char *DetermineDefaultGame();

	bool m_bEditMode;
};


//-----------------------------------------------------------------------------
// The dirty disk error report function
//-----------------------------------------------------------------------------
void ReportDirtyDiskNoMaterialSystem()
{
#ifdef _X360
	for ( int i = 0; i < 4; ++i )
	{
		if ( XUserGetSigninState( i ) != eXUserSigninState_NotSignedIn )
		{
			XShowDirtyDiscErrorUI( i );
			return;
		}
	}
	XShowDirtyDiscErrorUI( 0 );
#endif
}


//-----------------------------------------------------------------------------
// Instantiate all main libraries
//-----------------------------------------------------------------------------
bool CSourceAppSystemGroup::Create()
{
	IFileSystem *pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
	pFileSystem->InstallDirtyDiskReportFunc( ReportDirtyDiskNoMaterialSystem );

#ifdef WIN32
	CoInitialize( NULL );
#endif

	// Are we running in edit mode?
	m_bEditMode = CommandLine()->CheckParm( "-edit" );

	double st = Plat_FloatTime();

	AppSystemInfo_t appSystems[] = 
	{
		{ "engine" DLL_EXT_STRING,			CVAR_QUERY_INTERFACE_VERSION },	// NOTE: This one must be first!!
		{ "inputsystem" DLL_EXT_STRING,		INPUTSYSTEM_INTERFACE_VERSION },
		{ "materialsystem" DLL_EXT_STRING,	MATERIAL_SYSTEM_INTERFACE_VERSION },
		{ "datacache" DLL_EXT_STRING,		DATACACHE_INTERFACE_VERSION },
		{ "datacache" DLL_EXT_STRING,		MDLCACHE_INTERFACE_VERSION },
		{ "datacache" DLL_EXT_STRING,		STUDIO_DATA_CACHE_INTERFACE_VERSION },
		{ "studiorender" DLL_EXT_STRING,	STUDIO_RENDER_INTERFACE_VERSION },
		{ "vphysics" DLL_EXT_STRING,		VPHYSICS_INTERFACE_VERSION },
		{ "video_services" DLL_EXT_STRING,  VIDEO_SERVICES_INTERFACE_VERSION },
  
		// NOTE: This has to occur before vgui2.dll so it replaces vgui2's surface implementation
		{ "vguimatsurface" DLL_EXT_STRING,	VGUI_SURFACE_INTERFACE_VERSION },
		{ "vgui2" DLL_EXT_STRING,			VGUI_IVGUI_INTERFACE_VERSION },
		{ "engine" DLL_EXT_STRING,			VENGINE_LAUNCHER_API_VERSION },

		{ "", "" }							// Required to terminate the list
	};

#if defined( USE_SDL )
	AddSystem( (IAppSystem *)CreateSDLMgr(), SDLMGR_INTERFACE_VERSION );
#endif

	if ( !AddSystems( appSystems ) ) 
		return false;
	
	// This will be NULL for games that don't support VR. That's ok. Just don't load the DLL
	AppModule_t sourceVRModule = LoadModule( "sourcevr" DLL_EXT_STRING );
	if( sourceVRModule != APP_MODULE_INVALID )
	{
		AddSystem( sourceVRModule, SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION );
	}

	// pull in our filesystem dll to pull the queued loader from it, we need to do it this way due to the 
	// steam/stdio split for our steam filesystem
	char pFileSystemDLL[MAX_PATH];
	bool bSteam;
	if ( FileSystem_GetFileSystemDLLName( pFileSystemDLL, MAX_PATH, bSteam ) != FS_OK )
		return false;

	AppModule_t fileSystemModule = LoadModule( pFileSystemDLL );
	AddSystem( fileSystemModule, QUEUEDLOADER_INTERFACE_VERSION );

	// Hook in datamodel and p4 control if we're running with -tools
	if ( IsPC() && ( ( CommandLine()->FindParm( "-tools" ) && !CommandLine()->FindParm( "-nop4" ) ) || CommandLine()->FindParm( "-p4" ) ) )
	{
#ifdef STAGING_ONLY
		AppModule_t p4libModule = LoadModule( "p4lib" DLL_EXT_STRING );
		IP4 *p4 = (IP4*)AddSystem( p4libModule, P4_INTERFACE_VERSION );
		
		// If we are running with -steam then that means the tools are being used by an SDK user. Don't exit in this case!
		if ( !p4 && !CommandLine()->FindParm( "-steam" ) )
		{
			return false;
		}
#endif // STAGING_ONLY

		AppModule_t vstdlibModule = LoadModule( "vstdlib" DLL_EXT_STRING );
		IProcessUtils *processUtils = ( IProcessUtils* )AddSystem( vstdlibModule, PROCESS_UTILS_INTERFACE_VERSION );
		if ( !processUtils )
			return false;
	}

	// Connect to iterfaces loaded in AddSystems that we need locally
	IMaterialSystem *pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
	if ( !pMaterialSystem )
		return false;

	g_pEngineAPI = (IEngineAPI*)FindSystem( VENGINE_LAUNCHER_API_VERSION );

	// Load the hammer DLL if we're in editor mode
#if defined( _WIN32 ) && defined( STAGING_ONLY )
	if ( m_bEditMode )
	{
		AppModule_t hammerModule = LoadModule( "hammer_dll" DLL_EXT_STRING );
		g_pHammer = (IHammer*)AddSystem( hammerModule, INTERFACEVERSION_HAMMER );
		if ( !g_pHammer )
		{
			return false;
		}
	}
#endif // defined( _WIN32 ) && defined( STAGING_ONLY )

	// Load up the appropriate shader DLL
	// This has to be done before connection.
	char const* pDLLName = "shaderapidx9" DLL_EXT_STRING;
	if ( CommandLine()->FindParm( "-noshaderapi" ) )
	{
		pDLLName = "shaderapiempty" DLL_EXT_STRING;
	}

	pMaterialSystem->SetShaderAPI( pDLLName );

	double elapsed = Plat_FloatTime() - st;
	COM_TimestampedLog( "LoadAppSystems:  Took %.4f secs to load libraries and get factories.", (float)elapsed );

	return true;
}

bool CSourceAppSystemGroup::PreInit()
{
	CreateInterfaceFn factory = GetFactory();
	ConnectTier1Libraries( &factory, 1 );
	ConVar_Register( );
	ConnectTier2Libraries( &factory, 1 );
	ConnectTier3Libraries( &factory, 1 );

	if ( !g_pFullFileSystem || !g_pMaterialSystem )
		return false;

	CFSSteamSetupInfo steamInfo;
	steamInfo.m_bToolsMode = false;
	steamInfo.m_bSetSteamDLLPath = false;
	steamInfo.m_bSteam = g_pFullFileSystem->IsSteam();
	steamInfo.m_bOnlyUseDirectoryName = true;
	steamInfo.m_pDirectoryName = DetermineDefaultMod();
	if ( !steamInfo.m_pDirectoryName )
	{
		steamInfo.m_pDirectoryName = DetermineDefaultGame();
		if ( !steamInfo.m_pDirectoryName )
		{
			Error( "FileSystem_LoadFileSystemModule: no -defaultgamedir or -game specified." );
		}
	}
	if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
		return false;

	CFSMountContentInfo fsInfo;
	fsInfo.m_pFileSystem = g_pFullFileSystem;
	fsInfo.m_bToolsMode = m_bEditMode;
	fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
	if ( FileSystem_MountContent( fsInfo ) != FS_OK )
		return false;

	if ( IsPC() || !IsX360() )
	{
		fsInfo.m_pFileSystem->AddSearchPath( "platform", "PLATFORM" );
	}
	else
	{
		// 360 needs absolute paths
		FileSystem_AddSearchPath_Platform( g_pFullFileSystem, steamInfo.m_GameInfoPath );
	}

	if ( IsPC() )
	{
		// This will get called multiple times due to being here, but only the first one will do anything
		reslistgenerator->Init( GetBaseDirectory(), CommandLine()->ParmValue( "-game", "hl2" ) );

		// This will also get called each time, but will actually fix up the command line as needed
		reslistgenerator->SetupCommandLine();
	}

	// FIXME: Logfiles is mod-specific, needs to move into the engine.
	g_LogFiles.Init();

	// Required to run through the editor
	if ( m_bEditMode )
	{
		g_pMaterialSystem->EnableEditorMaterials();	
	}

	StartupInfo_t info;
	info.m_pInstance = GetAppInstance();
	info.m_pBaseDirectory = GetBaseDirectory();
	info.m_pInitialMod = DetermineDefaultMod();
	info.m_pInitialGame = DetermineDefaultGame();
	info.m_pParentAppSystemGroup = this;
	info.m_bTextMode = g_bTextMode;

	g_pEngineAPI->SetStartupInfo( info );

	return true;
}

int CSourceAppSystemGroup::Main()
{
	return g_pEngineAPI->Run();
}

void CSourceAppSystemGroup::PostShutdown()
{
	// FIXME: Logfiles is mod-specific, needs to move into the engine.
	g_LogFiles.Shutdown();

	reslistgenerator->Shutdown();

	DisconnectTier3Libraries();
	DisconnectTier2Libraries();
	ConVar_Unregister( );
	DisconnectTier1Libraries();
}

void CSourceAppSystemGroup::Destroy() 
{
	g_pEngineAPI = NULL;
	g_pMaterialSystem = NULL;
	g_pHammer = NULL;

#ifdef WIN32
	CoUninitialize();
#endif
}


//-----------------------------------------------------------------------------
// Determines the initial mod to use at load time.
// We eventually (hopefully) will be able to switch mods at runtime
// because the engine/hammer integration really wants this feature.
//-----------------------------------------------------------------------------
const char *CSourceAppSystemGroup::DetermineDefaultMod()
{
	if ( !m_bEditMode )
	{   		 
		return CommandLine()->ParmValue( "-game", DEFAULT_HL2_GAMEDIR );
	}
	return g_pHammer->GetDefaultMod();
}

const char *CSourceAppSystemGroup::DetermineDefaultGame()
{
	if ( !m_bEditMode )
	{
		return CommandLine()->ParmValue( "-defaultgamedir", DEFAULT_HL2_GAMEDIR );
	}
	return g_pHammer->GetDefaultGame();
}

//-----------------------------------------------------------------------------
// MessageBox for SDL/OSX
//-----------------------------------------------------------------------------
#if defined( USE_SDL ) && !defined( _WIN32 )

int MessageBox( HWND hWnd, const char *message, const char *header, unsigned uType )
{
	SDL_ShowSimpleMessageBox( 0, header, message, GetAssertDialogParent() );
	return 0;
}

#endif

//-----------------------------------------------------------------------------
// Allow only one windowed source app to run at a time
//-----------------------------------------------------------------------------
#ifdef WIN32
HANDLE g_hMutex = NULL;
#elif defined(POSIX)
int g_lockfd = -1;
char g_lockFilename[MAX_PATH];
#endif
bool GrabSourceMutex()
{
#ifdef WIN32
	if ( IsPC() )
	{
		// don't allow more than one instance to run
		g_hMutex = ::CreateMutex(NULL, FALSE, TEXT("hl2_singleton_mutex"));

		unsigned int waitResult = ::WaitForSingleObject(g_hMutex, 0);

		// Here, we have the mutex
		if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED)
			return true;

		// couldn't get the mutex, we must be running another instance
		::CloseHandle(g_hMutex);

		return false;
	}
#elif defined(POSIX)

	// Under OSX use flock in /tmp/source_engine_<game>.lock, create the file if it doesn't exist
	const char *pchGameParam = CommandLine()->ParmValue( "-game", DEFAULT_HL2_GAMEDIR );
	CRC32_t gameCRC;
	CRC32_Init(&gameCRC);
	CRC32_ProcessBuffer( &gameCRC, (void *)pchGameParam, Q_strlen( pchGameParam ) );
	CRC32_Final( &gameCRC );

#ifdef ANDROID
	return true;
#elif defined (LINUX)
	/*
	 * Linux
 	 */

	// Check TMPDIR environment variable for temp directory.
	char *tmpdir = getenv( "TMPDIR" );

	// If it's NULL, or it doesn't exist, or it isn't a directory, fallback to /tmp.
	struct stat buf;
	if( !tmpdir || stat( tmpdir, &buf ) || !S_ISDIR ( buf.st_mode ) )
		tmpdir = "/tmp";

	V_snprintf( g_lockFilename, sizeof(g_lockFilename), "%s/source_engine_%u.lock", tmpdir, gameCRC );

	g_lockfd = open( g_lockFilename, O_WRONLY | O_CREAT, 0666 );
	if ( g_lockfd == -1 )
	{
		printf( "open(%s) failed\n", g_lockFilename );
		return false;
	}

	struct flock fl;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 1;

	if ( fcntl ( g_lockfd, F_SETLK, &fl ) == -1 )
	{
		printf( "fcntl(%d) for %s failed\n", g_lockfd, g_lockFilename );
		return false;
	}

	return true;
#else
	/*
	 * OSX
 	 */
	V_snprintf( g_lockFilename, sizeof(g_lockFilename), "/tmp/source_engine_%u.lock", gameCRC );

	g_lockfd = open( g_lockFilename, O_CREAT | O_WRONLY | O_EXLOCK | O_NONBLOCK | O_TRUNC, 0777 );
	if (g_lockfd >= 0)
	{
		// make sure we give full perms to the file, we only one instance per machine
		fchmod( g_lockfd, 0777 );

		// we leave the file open, under unix rules when we die we'll automatically close and remove the locks
		return true;
	}   		 

	// We were unable to open the file, it should be because we are unable to retain a lock
	if ( errno != EWOULDBLOCK)
	{
		fprintf( stderr, "unexpected error %d trying to exclusively lock %s\n", errno, g_lockFilename );
	}

	return false;
#endif // OSX

#endif // POSIX
	return true;
}

void ReleaseSourceMutex()
{
#ifdef WIN32
	if ( IsPC() && g_hMutex )
	{
		::ReleaseMutex( g_hMutex );
		::CloseHandle( g_hMutex );
		g_hMutex = NULL;
	}
#elif defined(POSIX)
	if ( g_lockfd != -1 )
	{
		close( g_lockfd );
		g_lockfd = -1;
		unlink( g_lockFilename ); 
	}
#endif
}

// Remove all but the last -game parameter.
// This is for mods based off something other than Half-Life 2 (like HL2MP mods).
// The Steam UI does 'steam -applaunch 320 -game c:\steam\steamapps\sourcemods\modname', but applaunch inserts
// its own -game parameter, which would supercede the one we really want if we didn't intercede here.
void RemoveSpuriousGameParameters()
{
	// Find the last -game parameter.
	int nGameArgs = 0;
	char lastGameArg[MAX_PATH];
	for ( int i=0; i < CommandLine()->ParmCount()-1; i++ )
	{
		if ( Q_stricmp( CommandLine()->GetParm( i ), "-game" ) == 0 )
		{
			Q_snprintf( lastGameArg, sizeof( lastGameArg ), "\"%s\"", CommandLine()->GetParm( i+1 ) );
			++nGameArgs;
			++i;
		}
	}

	// We only care if > 1 was specified.
	if ( nGameArgs > 1 )
	{
		CommandLine()->RemoveParm( "-game" );
		CommandLine()->AppendParm( "-game", lastGameArg );
	}
}

/*
============
va

does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
============
*/
static char *va( char *format, ... )
{
	va_list		argptr;
	static char	string[8][512];
	static int	curstring = 0;

	curstring = ( curstring + 1 ) % 8;

	va_start (argptr, format);
	Q_vsnprintf( string[curstring], sizeof( string[curstring] ), format, argptr );
	va_end (argptr);

	return string[curstring];  
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *param - 
// Output : static char const
//-----------------------------------------------------------------------------
static char const *Cmd_TranslateFileAssociation(char const *param )
{
	static char sz[ 512 ];
	char *retval = NULL;

	char temp[ 512 ];
	Q_strncpy( temp, param, sizeof( temp ) );
	Q_FixSlashes( temp );
#ifdef WIN32
	Q_strlower( temp );
#endif
	const char *extension = V_GetFileExtension(temp);
	// must have an extension to map
	if (!extension)
		return retval;
	extension--; // back up so we have the . in the extension

	int c = ARRAYSIZE( g_FileAssociations );
	for ( int i = 0; i < c; i++ )
	{
		FileAssociationInfo& info = g_FileAssociations[ i ];

		if ( ! Q_strcmp( extension, info.extension ) && 
			! CommandLine()->FindParm(va( "+%s", info.command_to_issue ) ) )
		{
			// Translate if haven't already got one of these commands			
			Q_strncpy( sz, temp, sizeof( sz ) );
			Q_FileBase( sz, temp, sizeof( sz ) );

			Q_snprintf( sz, sizeof( sz ), "%s %s", info.command_to_issue, temp );
			retval = sz;
			break;
		}		
	}

	// return null if no translation, otherwise return commands
	return retval;
}

//-----------------------------------------------------------------------------
// Purpose: Converts all the convar args into a convar command
// Input  : none 
// Output : const char * series of convars
//-----------------------------------------------------------------------------
static const char *BuildCommand()
{
	static CUtlBuffer build( 0, 0, CUtlBuffer::TEXT_BUFFER );
	build.Clear();

	// arg[0] is the executable name
	for ( int i=1; i < CommandLine()->ParmCount(); i++ )
	{
		const char *szParm = CommandLine()->GetParm(i);
		if (!szParm) continue;

		if (szParm[0] == '-') 
		{
			// skip -XXX options and eat their args
			const char *szValue = CommandLine()->ParmValue(szParm);
			if ( szValue ) i++;
			continue;
		}
		if (szParm[0] == '+')
		{
			// convert +XXX options and stuff them into the build buffer
			const char *szValue = CommandLine()->ParmValue(szParm);
			if (szValue)
			{
				build.PutString(va("%s %s;", szParm+1, szValue));
				i++;
			}
			else
			{
				build.PutString(szParm+1);
				build.PutChar(';');
			}
		}
		else 
		{
			// singleton values, convert to command
			char const *translated = Cmd_TranslateFileAssociation( CommandLine()->GetParm( i ) );
			if (translated)
			{
				build.PutString(translated);
				build.PutChar(';');
			}
		}
	}

	build.PutChar( '\0' );

	return (const char *)build.Base();
}

extern void InitGL4ES();

//-----------------------------------------------------------------------------
// Purpose: The real entry point for the application
// Input  : hInstance - 
//			hPrevInstance - 
//			lpCmdLine - 
//			nCmdShow - 
// Output : int APIENTRY
//-----------------------------------------------------------------------------
#ifdef WIN32
extern "C" __declspec(DLL_EXPORT) int LauncherMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
#else
DLL_EXPORT int LauncherMain( int argc, char **argv )
#endif
{
#if defined LINUX && !defined ANDROID
	// Temporary fix to stop us from crashing in printf/sscanf functions that don't expect
	//  localization to mess with your "." and "," float seperators. Mac OSX also sets LANG
	//  to en_US.UTF-8 before starting up (in info.plist I believe).
	// We need to double check that localization for libcef is handled correctly
	//  when we slam things to en_US.UTF-8.
	// Also check if C.UTF-8 exists and use it? This file: /usr/lib/locale/C.UTF-8.
	// It looks like it's only installed on Debian distros right now though.
	const char en_US[] = "en_US.UTF-8";

	setenv( "LC_ALL", en_US, 1 );
	setlocale( LC_ALL, en_US );

	const char *CurrentLocale = setlocale( LC_ALL, NULL );
	if ( Q_stricmp( CurrentLocale, en_US ) )
	{
		Msg( "WARNING: setlocale('%s') failed, using locale:'%s'. International characters may not work.\n", en_US, CurrentLocale );
	}

#endif // LINUX

#if defined LINUX && defined USE_SDL && defined TOGLES && !defined ANDROID
	SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
#endif

#ifdef WIN32
	SetAppInstance( hInstance );
#elif defined( POSIX )
	// Store off command line for argument searching
	Plat_SetCommandLine( BuildCmdLine( argc, argv, false ) );

	if( CommandLine()->CheckParm( "-sleepatstartup" ) )
	{
		// When launching from Steam, it can be difficult to get a debugger attached when you're
		//	crashing quickly at startup. So add a -sleepatstartup command line and sleep for 5
		//	seconds which should allow time to attach a debugger.
		sleep( 5 );
	}
#endif

	// Hook the debug output stuff.
	SpewOutputFunc( LauncherDefaultSpewFunc );

	if ( 0 && IsWin98OrOlder() )
	{
		Error( "This build does not currently run under Windows 98/Me." );
		return -1;
	}

	// Quickly check the hardware key, essentially a warning shot.  
	if ( !Plat_VerifyHardwareKeyPrompt() )
	{
		return -1;
	}
	
	const char *filename;
#ifdef WIN32
	CommandLine()->CreateCmdLine( IsPC() ? VCRHook_GetCommandLine() : lpCmdLine );
#else
	CommandLine()->CreateCmdLine( argc, argv );
#endif

	// No -dxlevel or +mat_hdr_level allowed on POSIX
#ifdef POSIX	
	CommandLine()->RemoveParm( "-dxlevel" );
	CommandLine()->RemoveParm( "+mat_hdr_level" );
	CommandLine()->RemoveParm( "+mat_dxlevel" );
#endif

	// If we're using -default command line parameters, get rid of DX8 settings. 
	if ( CommandLine()->CheckParm( "-default" ) )
	{
		CommandLine()->RemoveParm( "-dxlevel" );
		CommandLine()->RemoveParm( "-maxdxlevel" );
		CommandLine()->RemoveParm( "+mat_dxlevel" );
	}
	
	// Figure out the directory the executable is running from
	UTIL_ComputeBaseDir();

#if defined( _X360 )
	bool bSpewDllInfo = CommandLine()->CheckParm( "-dllinfo" );
	bool bWaitForConsole = CommandLine()->CheckParm( "-vxconsole" );
	XboxConsoleInit();
	XBX_InitConsoleMonitor( bWaitForConsole || bSpewDllInfo );
#endif


#if defined( _X360 )
	if ( bWaitForConsole )
		COM_TimestampedLog( "LauncherMain: Application Start - %s", CommandLine()->GetCmdLine() );
	if ( bSpewDllInfo )
	{	
		XBX_DumpDllInfo( GetBaseDirectory() );
		Error( "Stopped!\n" );
	}

	int storageID = XboxLaunch()->GetStorageID();
	if ( storageID != XBX_INVALID_STORAGE_ID && storageID != XBX_STORAGE_DECLINED )
	{
		// Validate the storage device
		XDEVICE_DATA deviceData;
		DWORD ret = XContentGetDeviceData( storageID, &deviceData );
		if ( ret != ERROR_SUCCESS )
		{
			// Device was removed
			storageID = XBX_INVALID_STORAGE_ID;
			XBX_QueueEvent( XEV_LISTENER_NOTIFICATION, WM_SYS_STORAGEDEVICESCHANGED, 0, 0 );
		}
	}
	XBX_SetStorageDeviceId( storageID );

	int userID = XboxLaunch()->GetUserID();
	if ( !IsRetail() && userID == XBX_INVALID_USER_ID )
	{
		// didn't come from appchooser, try find a valid user id for dev purposes
		XUSER_SIGNIN_INFO info;
		for ( int i = 0; i < 4; ++i )
		{
			if ( ERROR_NO_SUCH_USER != XUserGetSigninInfo( i, 0, &info ) )
			{
				userID = i;
				break;
			}
		}
	}
	XBX_SetPrimaryUserId( userID );
#endif // defined( _X360 )
	
#ifdef POSIX
	{
		struct stat st;
		if ( stat( RELAUNCH_FILE, &st ) == 0 ) 
		{
			unlink( RELAUNCH_FILE );
		}
	}
#endif

	// This call is to emulate steam's injection of the GameOverlay DLL into our process if we
	// are running from the command line directly, this allows the same experience the user gets
	// to be present when running from perforce, the call has no effect on X360
	TryToLoadSteamOverlayDLL();

	// Start VCR mode?
	if ( CommandLine()->CheckParm( "-vcrrecord", &filename ) )
	{
		if ( !VCRStart( filename, true, &g_VCRHelpers ) )
		{
			Error( "-vcrrecord: can't open '%s' for writing.\n", filename );
			return -1;
		}
	}
	else if ( CommandLine()->CheckParm( "-vcrplayback", &filename ) )
	{
		if ( !VCRStart( filename, false, &g_VCRHelpers ) )
		{
			Error( "-vcrplayback: can't open '%s' for reading.\n", filename );
			return -1;
		}
	}

	// See the function for why we do this.
	RemoveSpuriousGameParameters();

#ifdef WIN32
	if ( IsPC() )
	{
		// initialize winsock
		WSAData wsaData;
		int	nError = ::WSAStartup( MAKEWORD(2,0), &wsaData );
		if ( nError )
		{
			Msg( "Warning! Failed to start Winsock via WSAStartup = 0x%x.\n", nError);
		}
	}
#endif

	// Run in text mode? (No graphics or sound).
	if ( CommandLine()->CheckParm( "-textmode" ) )
	{
		g_bTextMode = true;
		InitTextMode();
	}
#ifdef WIN32
	else
	{
		int retval = -1;
		// Can only run one windowed source app at a time
		if ( !GrabSourceMutex() )
		{
			// Allow the user to explicitly say they want to be able to run multiple instances of the source mutex.
			// Useful for side-by-side comparisons of different renderers.
			bool multiRun = CommandLine()->CheckParm( "-multirun" ) != NULL;

			// We're going to hijack the existing session and load a new savegame into it. This will mainly occur when users click on links in Bugzilla that will automatically copy saves and load them
			// directly from the web browser. The -hijack command prevents the launcher from objecting that there is already an instance of the game.
			if (CommandLine()->CheckParm( "-hijack" ))
			{
				HWND hwndEngine = FindWindow( "Valve001", NULL );

				// Can't find the engine
				if ( hwndEngine == NULL )
				{
					::MessageBox( NULL, "The modified entity keyvalues could not be sent to the Source Engine because the engine does not appear to be running.", "Source Engine Not Running", MB_OK | MB_ICONEXCLAMATION );
				}
				else
				{			
					const char *szCommand = BuildCommand();

					//
					// Fill out the data structure to send to the engine.
					//
					COPYDATASTRUCT copyData;
					copyData.cbData = strlen( szCommand ) + 1;
					copyData.dwData = 0;
					copyData.lpData = ( void * )szCommand;

					if ( !::SendMessage( hwndEngine, WM_COPYDATA, 0, (LPARAM)&copyData ) )
					{
						::MessageBox( NULL, "The Source Engine was found running, but did not accept the request to load a savegame. It may be an old version of the engine that does not support this functionality.", "Source Engine Declined Request", MB_OK | MB_ICONEXCLAMATION );
					}
					else
					{
						retval = 0;
					}

					free((void *)szCommand);
				}
			}
			else
			{
				if (!multiRun) {
					::MessageBox(NULL, "Only one instance of the game can be running at one time.", "Source - Warning", MB_ICONINFORMATION | MB_OK);
				}
			}

			if (!multiRun) {
				return retval;
			}
		}
	}
#elif defined( POSIX )
	else
	{
		if ( !GrabSourceMutex() )
		{
			::MessageBox(NULL, "Only one instance of the game can be running at one time.", "Source - Warning", 0 );
			return -1;
		}
	}
#endif

#ifdef WIN32
	// Make low priority?
	if ( CommandLine()->CheckParm( "-low" ) )
	{
		SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
	}
	else if ( CommandLine()->CheckParm( "-high" ) )
	{
		SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
	}
#endif

	// If game is not run from Steam then add -insecure in order to avoid client timeout message
	if ( NULL == CommandLine()->CheckParm( "-steam" ) )
	{
		CommandLine()->AppendParm( "-insecure", NULL );
	}

	// Figure out the directory the executable is running from
	// and make that be the current working directory

	_chdir( GetBaseDirectory() );

	g_LeakDump.m_bCheckLeaks = CommandLine()->CheckParm( "-leakcheck" ) ? true : false;

	bool bRestart = true;
	while ( bRestart )
	{
		bRestart = false;

		CSourceAppSystemGroup sourceSystems;
		CSteamApplication steamApplication( &sourceSystems );
		int nRetval = steamApplication.Run();
		if ( steamApplication.GetErrorStage() == CSourceAppSystemGroup::INITIALIZATION )
		{
			bRestart = (nRetval == INIT_RESTART);
		}
		else if ( nRetval == RUN_RESTART )
		{
			bRestart = true;
		}

		bool bReslistCycle = false;
		if ( !bRestart )
		{
			bReslistCycle = reslistgenerator->ShouldContinue();
			bRestart = bReslistCycle;
		}
		
		if ( !bReslistCycle )
		{
			// Remove any overrides in case settings changed
			CommandLine()->RemoveParm( "-w" );
			CommandLine()->RemoveParm( "-h" );
			CommandLine()->RemoveParm( "-width" );
			CommandLine()->RemoveParm( "-height" );
			CommandLine()->RemoveParm( "-sw" );
			CommandLine()->RemoveParm( "-startwindowed" );
			CommandLine()->RemoveParm( "-windowed" );
			CommandLine()->RemoveParm( "-window" );
			CommandLine()->RemoveParm( "-full" );
			CommandLine()->RemoveParm( "-fullscreen" );
			CommandLine()->RemoveParm( "-dxlevel" );
			CommandLine()->RemoveParm( "-autoconfig" );
			CommandLine()->RemoveParm( "+mat_hdr_level" );
		}
	}

#ifdef WIN32
	if ( IsPC() )
	{
		// shutdown winsock
		int nError = ::WSACleanup();
		if ( nError )
		{
			Msg( "Warning! Failed to complete WSACleanup = 0x%x.\n", nError );
		}
	}
#endif

	// Allow other source apps to run
	ReleaseSourceMutex();

#if defined( WIN32 ) && !defined( _X360 )

	// Now that the mutex has been released, check HKEY_CURRENT_USER\Software\Valve\Source\Relaunch URL. If there is a URL here, exec it.
	// This supports the capability of immediately re-launching the the game via Steam in a different audio language 
	HKEY hKey; 
	if ( RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source", NULL, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
	{
		char szValue[MAX_PATH];
		DWORD dwValueLen = MAX_PATH;

		if ( RegQueryValueEx( hKey, "Relaunch URL", NULL, NULL, (unsigned char*)szValue, &dwValueLen ) == ERROR_SUCCESS )
		{
			ShellExecute (0, "open", szValue, 0, 0, SW_SHOW);
			RegDeleteValue( hKey, "Relaunch URL" );
		}

		RegCloseKey(hKey);
	}

#elif defined( OSX ) || defined( LINUX )
	struct stat st;
	if ( stat( RELAUNCH_FILE, &st ) == 0 ) 
	{
		FILE *fp = fopen( RELAUNCH_FILE, "r" );
		if ( fp )
		{
			char szCmd[256];
			int nChars = fread( szCmd, 1, sizeof(szCmd), fp );
			if ( nChars > 0 )
			{
				if ( nChars > (sizeof(szCmd)-1) )
				{
					nChars = (sizeof(szCmd)-1);
				}
				szCmd[nChars] = 0;
				char szOpenLine[ MAX_PATH ];
				#if defined( LINUX )
					Q_snprintf( szOpenLine, sizeof(szOpenLine), "xdg-open \"%s\"", szCmd );
				#else
					Q_snprintf( szOpenLine, sizeof(szOpenLine), "open \"%s\"", szCmd );
				#endif
				system( szOpenLine );
			}
			fclose( fp );
			unlink( RELAUNCH_FILE );
		}
	}
#elif defined( _X360 )
#else
#error
#endif

	return 0;
}