diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /launcher | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'launcher')
| -rw-r--r-- | launcher/ifilesystem.h | 37 | ||||
| -rw-r--r-- | launcher/launcher.cpp | 1566 | ||||
| -rw-r--r-- | launcher/launcher.vpc | 100 | ||||
| -rw-r--r-- | launcher/res/launcher.ico | bin | 0 -> 29926 bytes | |||
| -rw-r--r-- | launcher/reslistgenerator.cpp | 523 | ||||
| -rw-r--r-- | launcher/reslistgenerator.h | 32 | ||||
| -rw-r--r-- | launcher/xbox/xbox.def | 3 |
7 files changed, 2261 insertions, 0 deletions
diff --git a/launcher/ifilesystem.h b/launcher/ifilesystem.h new file mode 100644 index 0000000..75d9cfe --- /dev/null +++ b/launcher/ifilesystem.h @@ -0,0 +1,37 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// +#ifndef IFILESYSTEM_H +#define IFILESYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "interface.h" + + +class IFileSystem; + +//----------------------------------------------------------------------------- +// Loads, unloads the file system DLL +//----------------------------------------------------------------------------- +bool FileSystem_LoadFileSystemModule( void ); +void FileSystem_UnloadFileSystemModule( void ); + +CSysModule * FileSystem_LoadModule( const char* path ); +void FileSystem_UnloadModule( CSysModule *pModule ); + +bool FileSystem_Init( ); +void FileSystem_Shutdown( void ); + +// Sets the file system search path based on the game directory +bool FileSystem_SetGameDirectory( char const* pGameDir ); + +extern IFileSystem *g_pFileSystem; + +#endif // IFILESYSTEM_H
\ No newline at end of file diff --git a/launcher/launcher.cpp b/launcher/launcher.cpp new file mode 100644 index 0000000..d34ae11 --- /dev/null +++ b/launcher/launcher.cpp @@ -0,0 +1,1566 @@ +//========= 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 + +// 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 ) +{ + return g_szBasedir; +} + +//----------------------------------------------------------------------------- +// 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 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(); +} + +//----------------------------------------------------------------------------- +// Purpose: The real entry point for the application +// Input : hInstance - +// hPrevInstance - +// lpCmdLine - +// nCmdShow - +// Output : int APIENTRY +//----------------------------------------------------------------------------- +#ifdef WIN32 +extern "C" __declspec(dllexport) int LauncherMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +#else +DLL_EXPORT int LauncherMain( int argc, char **argv ) +#endif +{ +#ifdef LINUX + // 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 ) ) + { + Warning( "WARNING: setlocale('%s') failed, using locale:'%s'. International characters may not work.\n", en_US, CurrentLocale ); + } +#endif // LINUX + +#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)©Data ) ) + { + ::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; +} diff --git a/launcher/launcher.vpc b/launcher/launcher.vpc new file mode 100644 index 0000000..f3754b7 --- /dev/null +++ b/launcher/launcher.vpc @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// LAUNCHER.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $General + { + $AdditionalProjectDependencies "$BASE;togl" [$OSXALL] + } + $Compiler + { + $PreprocessorDefinitions "$BASE;LAUNCHERONLY" + } + + $Linker + { + $AdditionalDependencies "$BASE shlwapi.lib winmm.lib wsock32.lib odbc32.lib odbccp32.lib $SRCDIR\dx9sdk\lib\dinput8.lib $SRCDIR\dx9sdk\lib\ddraw.lib" [$WIN32] + $SystemLibraries "iconv" [$OSXALL] + $SystemFrameworks "Carbon;AppKit;OpenGL;IOKit" [$OSXALL] + // We run from the ./game dir, but want to look in the ./game/bin directory when loading libraries. + // To dump rpath/runpath of a library, use "chrpath --list blah.so" or "objdump -x blah.so" or "readelf -d bin/launcher.so" + $GCC_ExtraLinkerFlags "-Wl,--enable-new-dtags -Wl,-z,origin -Wl,-rpath,'$$ORIGIN' -L/usr/lib32 -L/usr/lib" [$LINUXALL] + } +} + +$Configuration "Debug" +{ + $Linker + { + $AdditionalDependencies "$BASE Xonlined.lib" [$X360] + } +} + +$Configuration "Release" +{ + $Linker + { + $AdditionalDependencies "$BASE Xonline.lib" [$X360] + } +} + +$Project "launcher" +{ + $Folder "Source Files" + { + $File "$SRCDIR\public\filesystem_init.cpp" + $File "launcher.cpp" + $File "reslistgenerator.cpp" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\tier0\basetypes.h" + $File "$SRCDIR\public\tier0\commonmacros.h" + $File "$SRCDIR\public\tier0\dbg.h" + $File "$SRCDIR\common\engine_launcher_api.h" + $File "$SRCDIR\public\tier0\fasttimer.h" + $File "$SRCDIR\public\appframework\IAppSystem.h" + $File "$SRCDIR\public\tier0\icommandline.h" + $File "ifilesystem.h" + $File "$SRCDIR\public\vgui\IHTML.h" + $File "$SRCDIR\public\vgui\IImage.h" + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\public\vgui\ISurface.h" + $File "$SRCDIR\public\vgui\KeyCode.h" + $File "$SRCDIR\public\tier0\mem.h" + $File "$SRCDIR\public\tier0\memalloc.h" + $File "$SRCDIR\public\vgui\MouseCode.h" + $File "$SRCDIR\public\tier0\platform.h" + $File "$SRCDIR\public\tier0\protected_things.h" + $File "reslistgenerator.h" + $File "$SRCDIR\public\string_t.h" + $File "$SRCDIR\public\tier1\strtools.h" + $File "$SRCDIR\public\tier0\vcr_shared.h" + $File "$SRCDIR\public\tier0\vcrmode.h" + $File "$SRCDIR\public\mathlib\vector2d.h" + $File "$SRCDIR\public\vgui\VGUI.h" + $File "$SRCDIR\public\vstdlib\vstdlib.h" + } + + $folder "Link Libraries" + { + $Lib appframework + $Lib tier2 + $Lib tier3 + $Implib steam_api + $ImpLib togl [!$IS_LIB_PROJECT && $GL] + $ImpLib SDL2 [$SDL] + } + +} + diff --git a/launcher/res/launcher.ico b/launcher/res/launcher.ico Binary files differnew file mode 100644 index 0000000..bda52dd --- /dev/null +++ b/launcher/res/launcher.ico diff --git a/launcher/reslistgenerator.cpp b/launcher/reslistgenerator.cpp new file mode 100644 index 0000000..b7a08a5 --- /dev/null +++ b/launcher/reslistgenerator.cpp @@ -0,0 +1,523 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// Defines the entry point for the application. +// +//===========================================================================// + +#include "reslistgenerator.h" +#include "filesystem.h" +#include "tier1/utlrbtree.h" +#include "tier1/fmtstr.h" +#include "characterset.h" +#include "tier1/utlstring.h" +#include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier0/icommandline.h" +#include "tier1/KeyValues.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +bool SaveResList( const CUtlRBTree< CUtlString, int > &list, char const *pchFileName, char const *pchSearchPath ) +{ + FileHandle_t fh = g_pFullFileSystem->Open( pchFileName, "wt", pchSearchPath ); + if ( fh != FILESYSTEM_INVALID_HANDLE ) + { + for ( int i = list.FirstInorder(); i != list.InvalidIndex(); i = list.NextInorder( i ) ) + { + g_pFullFileSystem->Write( list[ i ].String(), Q_strlen( list[ i ].String() ), fh ); + g_pFullFileSystem->Write( "\n", 1, fh ); + } + + g_pFullFileSystem->Close( fh ); + return true; + } + return false; +} + +void LoadResList( CUtlRBTree< CUtlString, int > &list, char const *pchFileName, char const *pchSearchPath ) +{ + CUtlBuffer buffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); + if ( !g_pFullFileSystem->ReadFile( pchFileName, pchSearchPath, buffer ) ) + { + // does not exist + return; + } + + characterset_t breakSet; + CharacterSetBuild( &breakSet, "" ); + + // parse reslist + char szToken[ MAX_PATH ]; + for ( ;; ) + { + int nTokenSize = buffer.ParseToken( &breakSet, szToken, sizeof( szToken ) ); + if ( nTokenSize <= 0 ) + { + break; + } + + Q_strlower( szToken ); + Q_FixSlashes( szToken ); + + // Ensure filename has "quotes" around it + CUtlString s; + if ( szToken[ 0 ] == '\"' ) + { + Assert( Q_strlen( szToken ) > 2 ); + Assert( szToken[ Q_strlen( szToken ) - 1 ] == '\"' ); + s = szToken; + } + else + { + s = CFmtStr( "\"%s\"", szToken ); + } + + int idx = list.Find( s ); + if ( idx == list.InvalidIndex() ) + { + list.Insert( s ); + } + } +} + +static bool ReslistLogLessFunc( CUtlString const &pLHS, CUtlString const &pRHS ) +{ + return CaselessStringLessThan( pLHS.Get(), pRHS.Get() ); +} + +void SortResList( char const *pchFileName, char const *pchSearchPath ) +{ + CUtlRBTree< CUtlString, int > sorted( 0, 0, ReslistLogLessFunc ); + LoadResList( sorted, pchFileName, pchSearchPath ); + + // Now write it back out + SaveResList( sorted, pchFileName, pchSearchPath ); +} + +void MergeResLists( CUtlVector< CUtlString > &fileNames, char const *pchOutputFile, char const *pchSearchPath ) +{ + CUtlRBTree< CUtlString, int > sorted( 0, 0, ReslistLogLessFunc ); + for ( int i = 0; i < fileNames.Count(); ++i ) + { + LoadResList( sorted, fileNames[ i ].String(), pchSearchPath ); + } + + // Now write it back out + SaveResList( sorted, pchOutputFile, pchSearchPath ); +} + +class CWorkItem +{ +public: + CWorkItem() + { + } + + CUtlString m_sSubDir; + CUtlString m_sAddCommands; +}; +class CResListGenerator: public IResListGenerator +{ +public: + enum + { + STATE_BUILDINGRESLISTS = 0, + STATE_GENERATINGCACHES, + }; + + CResListGenerator(); + + virtual void Init( char const *pchBaseDir, char const *pchGameDir ); + virtual bool IsActive(); + virtual void Shutdown(); + virtual void Collate(); + + virtual void SetupCommandLine(); + virtual bool ShouldContinue(); + +private: + + bool InitCommandFile( char const *pchGameDir, char const *pchCommandFile ); + void LoadMapList( char const *pchGameDir, CUtlVector< CUtlString > &vecMaps, char const *pchMapFile ); + void CollateFiles( char const *pchResListFilename ); + + bool m_bInitialized; + bool m_bActive; + CUtlString m_sBaseDir; + CUtlString m_sGameDir; + CUtlString m_sFullGamePath; + + CUtlString m_sFinalDir; + CUtlString m_sWorkingDir; + CUtlString m_sBaseCommandLine; + CUtlString m_sOriginalCommandLine; + CUtlString m_sInitialStartMap; + + int m_nCurrentWorkItem; + CUtlVector< CWorkItem > m_WorkItems; + + CUtlVector< CUtlString > m_MapList; + int m_nCurrentState; +}; + +static CResListGenerator g_ResListGenerator; +IResListGenerator *reslistgenerator = &g_ResListGenerator; + +CResListGenerator::CResListGenerator() : + m_bInitialized( false ), + m_bActive( false ), + m_nCurrentWorkItem( 0 ), + m_nCurrentState( STATE_BUILDINGRESLISTS ) +{ + MEM_ALLOC_CREDIT(); + + m_sFinalDir = "reslists"; + m_sWorkingDir = "reslists_work"; +} + +void CResListGenerator::CollateFiles( char const *pchResListFilename ) +{ + CUtlVector< CUtlString > vecReslists; + + for ( int i = 0; i < m_WorkItems.Count(); ++i ) + { + char fn[ MAX_PATH ]; + Q_snprintf( fn, sizeof( fn ), "%s\\%s\\%s\\%s", m_sFullGamePath.String(), m_sWorkingDir.String(), m_WorkItems[ i ].m_sSubDir.String(), pchResListFilename ); + vecReslists.AddToTail( fn ); + } + + MergeResLists( vecReslists, CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sFinalDir.String(), pchResListFilename ), "GAME" ); +} + +void CResListGenerator::Init( char const *pchBaseDir, char const *pchGameDir ) +{ + if ( IsX360() ) + { + // not used or supported + return; + } + + // Because we have to call this inside the first Apps "PreInit", we need only Init on the very first call + if ( m_bInitialized ) + { + return; + } + + m_bInitialized = true; + + m_sBaseDir = pchBaseDir; + m_sGameDir = pchGameDir; + + char path[MAX_PATH]; + Q_snprintf( path, sizeof(path), "%s/%s", m_sBaseDir.String(), m_sGameDir.String() ); + Q_FixSlashes( path ); + Q_strlower( path ); + m_sFullGamePath = path; + + const char *pchCommandFile = NULL; + if ( CommandLine()->CheckParm( "-makereslists", &pchCommandFile ) && pchCommandFile ) + { + // base path setup, now can get and parse command file + InitCommandFile( path, pchCommandFile ); + } +} + +void CResListGenerator::Shutdown() +{ + if ( !m_bActive ) + return; +} + +bool CResListGenerator::IsActive() +{ + return m_bInitialized && m_bActive; +} + +void CResListGenerator::Collate() +{ + char szDir[MAX_PATH]; + V_snprintf( szDir, sizeof( szDir ), "%s\\%s", m_sFullGamePath.String(), m_sFinalDir.String() ); + g_pFullFileSystem->CreateDirHierarchy( szDir, "GAME" ); + + // Now create the collated/merged data + CollateFiles( "all.lst" ); + CollateFiles( "engine.lst" ); + for ( int i = 0 ; i < m_MapList.Count(); ++i ) + { + CollateFiles( CFmtStr( "%s.lst", m_MapList[ i ].String() ) ); + } +} + +void CResListGenerator::SetupCommandLine() +{ + if ( !m_bActive ) + return; + + switch ( m_nCurrentState ) + { + case STATE_BUILDINGRESLISTS: + { + Assert( m_nCurrentWorkItem < m_WorkItems.Count() ); + + const CWorkItem &work = m_WorkItems[ m_nCurrentWorkItem ]; + + // Clean the working dir + char szWorkingDir[ 512 ]; + Q_snprintf( szWorkingDir, sizeof( szWorkingDir ), "%s\\%s", m_sWorkingDir.String(), work.m_sSubDir.String() ); + + char szFullWorkingDir[MAX_PATH]; + V_snprintf( szFullWorkingDir, sizeof( szFullWorkingDir ), "%s\\%s", m_sFullGamePath.String(), szWorkingDir ); + g_pFullFileSystem->CreateDirHierarchy( szFullWorkingDir, "GAME" ); + + // Preserve startmap + char const *pszStartMap = NULL; + CommandLine()->CheckParm( "-startmap", &pszStartMap ); + char szMap[ MAX_PATH ] = { 0 }; + if ( pszStartMap ) + { + Q_strncpy( szMap, pszStartMap, sizeof( szMap ) ); + } + + // Prepare stuff + // Reset command line based on current state + char szCmd[ 512 ]; + Q_snprintf( szCmd, sizeof( szCmd ), "%s %s %s -reslistdir %s", m_sOriginalCommandLine.String(), m_sBaseCommandLine.String(), work.m_sAddCommands.String(), szWorkingDir ); + + Warning( "Reslists: Setting command line:\n'%s'\n", szCmd ); + + CommandLine()->CreateCmdLine( szCmd ); + // Never rebuild caches by default, inly do it in STATE_GENERATINGCACHES + CommandLine()->AppendParm( "-norebuildaudio", NULL ); + if ( szMap[ 0 ] ) + { + CommandLine()->AppendParm( "-startmap", szMap ); + } + } + break; + case STATE_GENERATINGCACHES: + { + Collate(); + + // Prepare stuff + // Reset command line based on current state + char szCmd[ 512 ]; + Q_snprintf( szCmd, sizeof( szCmd ), "%s -reslistdir %s -rebuildaudio", m_sOriginalCommandLine.String(), m_sFinalDir.String()); + + Warning( "Caches: Setting command line:\n'%s'\n", szCmd ); + + CommandLine()->CreateCmdLine( szCmd ); + + CommandLine()->RemoveParm( "-norebuildaudio" ); + CommandLine()->RemoveParm( "-makereslists" ); + + ++m_nCurrentState; + } + break; + } +} + +bool CResListGenerator::ShouldContinue() +{ + if ( !m_bActive ) + return false; + + bool bContinueAdvancing = false; + do + { + switch ( m_nCurrentState ) + { + default: + break; + case STATE_BUILDINGRESLISTS: + { + CommandLine()->RemoveParm( "-startmap" ); + + // Advance to next time + ++m_nCurrentWorkItem; + + if ( m_nCurrentWorkItem >= m_WorkItems.Count()) + { + // Will stay in the loop + ++m_nCurrentState; + bContinueAdvancing = true; + } + else + { + return true; + } + } + break; + case STATE_GENERATINGCACHES: + { + return true; + } + break; + } + } while ( bContinueAdvancing ); + + return false; +} + +void CResListGenerator::LoadMapList( char const *pchGameDir, CUtlVector< CUtlString > &vecMaps, char const *pchMapFile ) +{ + char fullpath[ 512 ]; + Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", pchGameDir, pchMapFile ); + + // Load them in + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + + if ( g_pFullFileSystem->ReadFile( fullpath, "GAME", buf ) ) + { + char szMap[ MAX_PATH ]; + while ( true ) + { + buf.GetLine( szMap, sizeof( szMap ) ); + if ( !szMap[ 0 ] ) + break; + + // Strip trailing CR/LF chars + int len = Q_strlen( szMap ); + while ( len >= 1 && ( szMap[ len - 1 ] == '\n' || szMap[ len - 1 ] == '\r' ) ) + { + szMap[ len - 1 ] = 0; + len = Q_strlen( szMap ); + } + + CUtlString newMap; + newMap = szMap; + vecMaps.AddToTail( newMap ); + } + } + else + { + Error( "Unable to maplist file %s\n", fullpath ); + } +} + +bool CResListGenerator::InitCommandFile( char const *pchGameDir, char const *pchCommandFile ) +{ + if ( *pchCommandFile == '+' || + *pchCommandFile == '-' ) + { + Msg( "falling back to legacy reslists system\n" ); + return false; + } + + char fullpath[ 512 ]; + Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", pchGameDir, pchCommandFile ); + + CUtlBuffer buf; + if ( !g_pFullFileSystem->ReadFile( fullpath, "GAME", buf ) ) + { + Error( "Unable to load '%s'\n", fullpath ); + return false; + } + + KeyValues *kv = new KeyValues( "reslists" ); + if ( !kv->LoadFromBuffer( "reslists", (const char *)buf.Base() ) ) + { + Error( "Unable to parse keyvalues from '%s'\n", fullpath ); + kv->deleteThis(); + return false; + } + + CUtlString sMapListFile = kv->GetString( "maplist", "maplist.txt" ); + LoadMapList( pchGameDir, m_MapList, sMapListFile ); + if ( m_MapList.Count() <= 0 ) + { + Error( "Maplist file '%s' empty or missing!!!\n", sMapListFile.String() ); + kv->deleteThis(); + return false; + } + + char const *pszSolo = NULL; + if ( CommandLine()->CheckParm( "+map", &pszSolo ) && pszSolo ) + { + m_MapList.Purge(); + + CUtlString newMap; + newMap = pszSolo; + m_MapList.AddToTail( newMap ); + } + + m_nCurrentWorkItem = CommandLine()->ParmValue( "-startstage", 0 ); + + char const *pszStartMap = NULL; + CommandLine()->CheckParm( "-startmap", &pszStartMap ); + if ( pszStartMap ) + { + m_sInitialStartMap = pszStartMap; + } + + CommandLine()->RemoveParm( "-startstage" ); + CommandLine()->RemoveParm( "-makereslists" ); + CommandLine()->RemoveParm( "-reslistdir" ); + CommandLine()->RemoveParm( "-norebuildaudio" ); + CommandLine()->RemoveParm( "-startmap" ); + + m_sOriginalCommandLine = CommandLine()->GetCmdLine(); + + // Add it back in for first map + if ( pszStartMap ) + { + CommandLine()->AppendParm( "-startmap", m_sInitialStartMap.String() ); + } + + m_sBaseCommandLine = kv->GetString( "basecommandline", "" ); + m_sFinalDir = kv->GetString( "finaldir", m_sFinalDir.String() ); + m_sWorkingDir = kv->GetString( "workdir", m_sWorkingDir.String() ); + + int i = 0; + do + { + char sz[ 32 ]; + Q_snprintf( sz, sizeof( sz ), "%i", i ); + KeyValues *subKey = kv->FindKey( sz, false ); + if ( !subKey ) + break; + + CWorkItem work; + + work.m_sSubDir = subKey->GetString( "subdir", "" ); + work.m_sAddCommands = subKey->GetString( "addcommands", "" ); + + if ( work.m_sSubDir.Length() > 0 ) + { + m_WorkItems.AddToTail( work ); + } + else + { + Error( "%s: failed to specify 'subdir' for item %s\n", fullpath, sz ); + } + + ++i; + } while ( true ); + + m_bActive = m_WorkItems.Count() > 0; + + m_nCurrentWorkItem = clamp( m_nCurrentWorkItem, 0, m_WorkItems.Count() - 1 ); + + bool bCollate = CommandLine()->CheckParm( "-collate" ) ? true : false; + if ( bCollate ) + { + Collate(); + m_bActive = false; + exit( -1 ); + } + + kv->deleteThis(); + + /* + if ( m_bActive ) + { + // Wipe console log + g_pFullFileSystem->RemoveFile( "console.log", "GAME" ); + } + */ + return m_bActive; +} + + diff --git a/launcher/reslistgenerator.h b/launcher/reslistgenerator.h new file mode 100644 index 0000000..2939fa8 --- /dev/null +++ b/launcher/reslistgenerator.h @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// Defines the entry point for the application. +// +//===========================================================================// + +#ifndef RESLISTGENERATOR_H +#define RESLISTGENERATOR_H + +#ifdef _WIN32 +#pragma once +#endif + +class IResListGenerator +{ +public: + + virtual void Init( char const *pchBaseDir, char const *pchGameDir ) = 0; + virtual void Shutdown() = 0; + virtual bool IsActive() = 0; + + virtual void SetupCommandLine() = 0; + + virtual bool ShouldContinue() = 0; +}; + +extern IResListGenerator *reslistgenerator; + +#endif // RESLISTGENERATOR_H + diff --git a/launcher/xbox/xbox.def b/launcher/xbox/xbox.def new file mode 100644 index 0000000..3b3e589 --- /dev/null +++ b/launcher/xbox/xbox.def @@ -0,0 +1,3 @@ +LIBRARY launcher_360.dll +EXPORTS + LauncherMain @1
\ No newline at end of file |