summaryrefslogtreecommitdiff
path: root/engine/sys_dll.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/sys_dll.cpp')
-rw-r--r--engine/sys_dll.cpp1600
1 files changed, 1600 insertions, 0 deletions
diff --git a/engine/sys_dll.cpp b/engine/sys_dll.cpp
new file mode 100644
index 0000000..a7795f4
--- /dev/null
+++ b/engine/sys_dll.cpp
@@ -0,0 +1,1600 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+
+#if defined(_WIN32) && !defined(_X360)
+#include "winlite.h"
+#elif defined(OSX)
+#include <Carbon/Carbon.h>
+#include <sys/sysctl.h>
+#endif
+#if defined(LINUX)
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+#if defined( USE_SDL )
+#include "SDL.h"
+#endif
+
+#include "quakedef.h"
+#include "igame.h"
+#include "errno.h"
+#include "host.h"
+#include "profiling.h"
+#include "server.h"
+#include "vengineserver_impl.h"
+#include "filesystem_engine.h"
+#include "sys.h"
+#include "sys_dll.h"
+#include "ivideomode.h"
+#include "host_cmd.h"
+#include "crtmemdebug.h"
+#include "sv_log.h"
+#include "sv_main.h"
+#include "traceinit.h"
+#include "dt_test.h"
+#include "keys.h"
+#include "gl_matsysiface.h"
+#include "tier0/vcrmode.h"
+#include "tier0/icommandline.h"
+#include "cmd.h"
+#include <ihltvdirector.h>
+#if defined( REPLAY_ENABLED )
+#include "replay/ireplaysystem.h"
+#endif
+#include "MapReslistGenerator.h"
+#include "DevShotGenerator.h"
+#include "cdll_engine_int.h"
+#include "dt_send.h"
+#include "idedicatedexports.h"
+#include "eifacev21.h"
+#include "cl_steamauth.h"
+#include "tier0/etwprof.h"
+
+#include "vgui_baseui_interface.h"
+#include "tier0/systeminformation.h"
+#ifdef _WIN32
+#if !defined( _X360 )
+#include <io.h>
+#endif
+#endif
+#include "toolframework/itoolframework.h"
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define ONE_HUNDRED_TWENTY_EIGHT_MB (128 * 1024 * 1024)
+
+ConVar mem_min_heapsize( "mem_min_heapsize", "48", FCVAR_INTERNAL_USE, "Minimum amount of memory to dedicate to engine hunk and datacache (in mb)" );
+ConVar mem_max_heapsize( "mem_max_heapsize", "256", FCVAR_INTERNAL_USE, "Maximum amount of memory to dedicate to engine hunk and datacache (in mb)" );
+ConVar mem_max_heapsize_dedicated( "mem_max_heapsize_dedicated", "64", FCVAR_INTERNAL_USE, "Maximum amount of memory to dedicate to engine hunk and datacache, for dedicated server (in mb)" );
+
+#define MINIMUM_WIN_MEMORY (unsigned)(mem_min_heapsize.GetInt()*1024*1024)
+#define MAXIMUM_WIN_MEMORY max( (unsigned)(mem_max_heapsize.GetInt()*1024*1024), MINIMUM_WIN_MEMORY )
+#define MAXIMUM_DEDICATED_MEMORY (unsigned)(mem_max_heapsize_dedicated.GetInt()*1024*1024)
+
+
+char *CheckParm(const char *psz, char **ppszValue = NULL);
+void SeedRandomNumberGenerator( bool random_invariant );
+void Con_ColorPrintf( const Color& clr, PRINTF_FORMAT_STRING const char *fmt, ... ) FMTFUNCTION( 2, 3 );
+
+void COM_ShutdownFileSystem( void );
+void COM_InitFilesystem( const char *pFullModPath );
+
+modinfo_t gmodinfo;
+
+#ifdef PLATFORM_WINDOWS
+HWND *pmainwindow = NULL;
+#endif
+
+char gszDisconnectReason[256];
+char gszExtendedDisconnectReason[256];
+bool gfExtendedError = false;
+uint8 g_eSteamLoginFailure = 0;
+bool g_bV3SteamInterface = false;
+CreateInterfaceFn g_AppSystemFactory = NULL;
+
+static bool s_bIsDedicated = false;
+ConVar *sv_noclipduringpause = NULL;
+
+// Special mode where the client uses a console window and has no graphics. Useful for stress-testing a server
+// without having to round up 32 people.
+bool g_bTextMode = false;
+
+// Set to true when we exit from an error.
+bool g_bInErrorExit = false;
+
+static FileFindHandle_t g_hfind = FILESYSTEM_INVALID_FIND_HANDLE;
+
+// The extension DLL directory--one entry per loaded DLL
+CSysModule *g_GameDLL = NULL;
+
+// Prototype of an global method function
+typedef void (DLLEXPORT * PFN_GlobalMethod)( edict_t *pEntity );
+
+IServerGameDLL *serverGameDLL = NULL;
+int g_iServerGameDLLVersion = 0;
+IServerGameEnts *serverGameEnts = NULL;
+
+IServerGameClients *serverGameClients = NULL;
+int g_iServerGameClientsVersion = 0; // This matches the number at the end of the interface name (so for "ServerGameClients004", this would be 4).
+
+IHLTVDirector *serverGameDirector = NULL;
+
+IServerGameTags *serverGameTags = NULL;
+
+void Sys_InitArgv( char *lpCmdLine );
+void Sys_ShutdownArgv( void );
+
+//-----------------------------------------------------------------------------
+// Purpose: Compare file times
+// Input : ft1 -
+// ft2 -
+// Output : int
+//-----------------------------------------------------------------------------
+int Sys_CompareFileTime( long ft1, long ft2 )
+{
+ if ( ft1 < ft2 )
+ {
+ return -1;
+ }
+ else if ( ft1 > ft2 )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is slash?
+//-----------------------------------------------------------------------------
+inline bool IsSlash( char c )
+{
+ return ( c == '\\') || ( c == '/' );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Create specified directory
+// Input : *path -
+// Output : void Sys_mkdir
+//-----------------------------------------------------------------------------
+void Sys_mkdir( const char *path )
+{
+ char testpath[ MAX_OSPATH ];
+
+ // Remove any terminal backslash or /
+ Q_strncpy( testpath, path, sizeof( testpath ) );
+ int nLen = Q_strlen( testpath );
+ if ( (nLen > 0) && IsSlash( testpath[ nLen - 1 ] ) )
+ {
+ testpath[ nLen - 1 ] = 0;
+ }
+
+ // Look for URL
+ const char *pPathID = "MOD";
+ if ( IsSlash( testpath[0] ) && IsSlash( testpath[1] ) )
+ {
+ pPathID = NULL;
+ }
+
+ if ( g_pFileSystem->FileExists( testpath, pPathID ) )
+ {
+ // if there is a file of the same name as the directory we want to make, just kill it
+ if ( !g_pFileSystem->IsDirectory( testpath, pPathID ) )
+ {
+ g_pFileSystem->RemoveFile( testpath, pPathID );
+ }
+ }
+
+ g_pFileSystem->CreateDirHierarchy( path, pPathID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *path -
+// *basename -
+// Output : char *Sys_FindFirst
+//-----------------------------------------------------------------------------
+const char *Sys_FindFirst(const char *path, char *basename, int namelength )
+{
+ if (g_hfind != FILESYSTEM_INVALID_FIND_HANDLE)
+ {
+ Sys_Error ("Sys_FindFirst without close");
+ g_pFileSystem->FindClose(g_hfind);
+ }
+
+ const char* psz = g_pFileSystem->FindFirst(path, &g_hfind);
+ if (basename && psz)
+ {
+ Q_FileBase(psz, basename, namelength );
+ }
+
+ return psz;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sys_FindFirst with a path ID filter.
+//-----------------------------------------------------------------------------
+const char *Sys_FindFirstEx( const char *pWildcard, const char *pPathID, char *basename, int namelength )
+{
+ if (g_hfind != FILESYSTEM_INVALID_FIND_HANDLE)
+ {
+ Sys_Error ("Sys_FindFirst without close");
+ g_pFileSystem->FindClose(g_hfind);
+ }
+
+ const char* psz = g_pFileSystem->FindFirstEx( pWildcard, pPathID, &g_hfind);
+ if (basename && psz)
+ {
+ Q_FileBase(psz, basename, namelength );
+ }
+
+ return psz;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *basename -
+// Output : char *Sys_FindNext
+//-----------------------------------------------------------------------------
+const char* Sys_FindNext(char *basename, int namelength)
+{
+ const char *psz = g_pFileSystem->FindNext(g_hfind);
+ if ( basename && psz )
+ {
+ Q_FileBase(psz, basename, namelength );
+ }
+
+ return psz;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : void Sys_FindClose
+//-----------------------------------------------------------------------------
+
+void Sys_FindClose(void)
+{
+ if ( FILESYSTEM_INVALID_FIND_HANDLE != g_hfind )
+ {
+ g_pFileSystem->FindClose(g_hfind);
+ g_hfind = FILESYSTEM_INVALID_FIND_HANDLE;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: OS Specific initializations
+//-----------------------------------------------------------------------------
+void Sys_Init( void )
+{
+ // Set default FPU control word to truncate (chop) mode for optimized _ftol()
+ // This does not "stick", the mode is restored somewhere down the line.
+// Sys_TruncateFPU();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Sys_Shutdown( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Print to system console
+// Input : *fmt -
+// ... -
+// Output : void Sys_Printf
+//-----------------------------------------------------------------------------
+void Sys_Printf(char *fmt, ...)
+{
+ va_list argptr;
+ char text[1024];
+
+ va_start (argptr,fmt);
+ Q_vsnprintf (text, sizeof( text ), fmt, argptr);
+ va_end (argptr);
+
+ if ( developer.GetInt() )
+ {
+#ifdef _WIN32
+ wchar_t unicode[2048];
+ ::MultiByteToWideChar(CP_UTF8, 0, text, -1, unicode, sizeof( unicode ) / sizeof(wchar_t));
+ unicode[(sizeof( unicode ) / sizeof(wchar_t)) - 1] = L'\0';
+ OutputDebugStringW( unicode );
+ Sleep( 0 );
+#else
+ fprintf( stderr, "%s", text );
+#endif
+ }
+
+ if ( s_bIsDedicated )
+ {
+ printf( "%s", text );
+ }
+}
+
+
+bool Sys_MessageBox(const char *title, const char *info, bool bShowOkAndCancel)
+{
+#ifdef _WIN32
+
+ if ( IDOK == ::MessageBox( NULL, title, info, MB_ICONEXCLAMATION | ( bShowOkAndCancel ? MB_OKCANCEL : MB_OK ) ) )
+ {
+ return true;
+ }
+ return false;
+
+#elif defined( USE_SDL )
+
+ int buttonid = 0;
+ SDL_MessageBoxData messageboxdata = { 0 };
+ SDL_MessageBoxButtonData buttondata[] =
+ {
+ { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "OK" },
+ { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "Cancel" },
+ };
+
+ messageboxdata.window = GetAssertDialogParent();
+ messageboxdata.title = title;
+ messageboxdata.message = info;
+ messageboxdata.numbuttons = bShowOkAndCancel ? 2 : 1;
+ messageboxdata.buttons = buttondata;
+
+ SDL_ShowMessageBox( &messageboxdata, &buttonid );
+ return ( buttonid == 1 );
+
+#elif defined( POSIX )
+
+ Warning( "%s\n", info );
+ return true;
+
+#else
+#error "implement me"
+#endif
+}
+
+bool g_bUpdateMinidumpComment = true;
+void BuildMinidumpComment( char const *pchSysErrorText, bool bRealCrash );
+
+void Sys_Error_Internal( bool bMinidump, const char *error, va_list argsList )
+{
+ char text[1024];
+ static bool bReentry = false; // Don't meltdown
+
+ Q_vsnprintf( text, sizeof( text ), error, argsList );
+
+ if ( bReentry )
+ {
+ fprintf( stderr, "%s\n", text );
+ return;
+ }
+
+ bReentry = true;
+
+ if ( s_bIsDedicated )
+ {
+ printf( "%s\n", text );
+ }
+ else
+ {
+ Sys_Printf( "%s\n", text );
+ }
+
+ // Write the error to the log and ensure the log contents get written to disk
+ g_Log.Printf( "Engine error: %s\n", text );
+ g_Log.Flush();
+
+ g_bInErrorExit = true;
+
+#if !defined( SWDS )
+ if ( IsPC() && videomode )
+ videomode->Shutdown();
+#endif
+
+ if ( IsPC() &&
+ !CommandLine()->FindParm( "-makereslists" ) &&
+ !CommandLine()->FindParm( "-nomessagebox" ) &&
+ !CommandLine()->FindParm( "-nocrashdialog" ) )
+ {
+#ifdef _WIN32
+ ::MessageBox( NULL, text, "Engine Error", MB_OK | MB_TOPMOST );
+#elif defined( USE_SDL )
+ Sys_MessageBox( "Engine Error", text, false );
+#endif
+ }
+
+ if ( IsPC() )
+ {
+ DebuggerBreakIfDebugging();
+ }
+ else if ( !IsRetail() )
+ {
+ DebuggerBreak();
+ }
+
+#if !defined( _X360 )
+
+ BuildMinidumpComment( text, true );
+ g_bUpdateMinidumpComment = false;
+
+ if ( bMinidump && !Plat_IsInDebugSession() && !CommandLine()->FindParm( "-nominidumps") )
+ {
+#if defined( WIN32 )
+ // MiniDumpWrite() has problems capturing the calling thread's context
+ // unless it is called with an exception context. So fake an exception.
+ __try
+ {
+ RaiseException
+ (
+ 0, // dwExceptionCode
+ EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
+ 0, // nNumberOfArguments,
+ NULL // const ULONG_PTR* lpArguments
+ );
+
+ // Never get here (non-continuable exception)
+ }
+ // Write the minidump from inside the filter (GetExceptionInformation() is only
+ // valid in the filter)
+ __except ( SteamAPI_WriteMiniDump( 0, GetExceptionInformation(), build_number() ), EXCEPTION_EXECUTE_HANDLER )
+ {
+
+ // We always get here because the above filter evaluates to EXCEPTION_EXECUTE_HANDLER
+ }
+#elif defined( OSX )
+ // Doing this doesn't quite work the way we want because there is no "crashing" thread
+ // and we see "No thread was identified as the cause of the crash; No signature could be created because we do not know which thread crashed" on the back end
+ //SteamAPI_WriteMiniDump( 0, NULL, build_number() );
+ printf("\n ##### Sys_Error: %s", text );
+ fflush(stdout );
+
+ int *p = 0;
+ *p = 0xdeadbeef;
+#elif defined( LINUX )
+ // Doing this doesn't quite work the way we want because there is no "crashing" thread
+ // and we see "No thread was identified as the cause of the crash; No signature could be created because we do not know which thread crashed" on the back end
+ //SteamAPI_WriteMiniDump( 0, NULL, build_number() );
+ int *p = 0;
+ *p = 0xdeadbeef;
+#else
+#warning "need minidump impl on sys_error"
+#endif
+ }
+
+#endif // _X360
+
+ host_initialized = false;
+#if defined(_WIN32) && !defined( _X360 )
+ // We don't want global destructors in our process OR in any DLL to get executed.
+ // _exit() avoids calling global destructors in our module, but not in other DLLs.
+ TerminateProcess( GetCurrentProcess(), 100 );
+#else
+ _exit( 100 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Exit engine with error
+// Input : *error -
+// ... -
+// Output : void Sys_Error
+//-----------------------------------------------------------------------------
+void Sys_Error(const char *error, ...)
+{
+ va_list argptr;
+
+ va_start( argptr, error );
+ Sys_Error_Internal( true, error, argptr );
+ va_end( argptr );
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Exit engine with error
+// Input : *error -
+// ... -
+// Output : void Sys_Error
+//-----------------------------------------------------------------------------
+void Sys_Exit(const char *error, ...)
+{
+ va_list argptr;
+
+ va_start( argptr, error );
+ Sys_Error_Internal( false, error, argptr );
+ va_end( argptr );
+
+}
+
+
+bool IsInErrorExit()
+{
+ return g_bInErrorExit;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : msec -
+// Output : void Sys_Sleep
+//-----------------------------------------------------------------------------
+void Sys_Sleep( int msec )
+{
+#ifdef _WIN32
+ Sleep ( msec );
+#elif POSIX
+ usleep( msec * 1000 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hInst -
+// ulInit -
+// lpReserved -
+// Output : BOOL WINAPI DllMain
+//-----------------------------------------------------------------------------
+#if defined(_WIN32) && !defined( _X360 )
+BOOL WINAPI DllMain(HANDLE hInst, ULONG ulInit, LPVOID lpReserved)
+{
+ InitCRTMemDebug();
+ if (ulInit == DLL_PROCESS_ATTACH)
+ {
+ }
+ else if (ulInit == DLL_PROCESS_DETACH)
+ {
+ }
+
+ return TRUE;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Allocate memory for engine hunk
+// Input : *parms -
+//-----------------------------------------------------------------------------
+void Sys_InitMemory( void )
+{
+ // Allow overrides
+ if ( CommandLine()->FindParm( "-minmemory" ) )
+ {
+ host_parms.memsize = MINIMUM_WIN_MEMORY;
+ return;
+ }
+
+ host_parms.memsize = 0;
+
+#ifdef _WIN32
+#if (_MSC_VER > 1200)
+ // MSVC 6.0 doesn't support GlobalMemoryStatusEx()
+ if ( IsPC() )
+ {
+ OSVERSIONINFOEX osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ if ( GetVersionEx ((OSVERSIONINFO *)&osvi) )
+ {
+ if ( osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5 )
+ {
+ MEMORYSTATUSEX memStat;
+ ZeroMemory(&memStat, sizeof(MEMORYSTATUSEX));
+ memStat.dwLength = sizeof(MEMORYSTATUSEX);
+ if ( GlobalMemoryStatusEx( &memStat ) )
+ {
+ if ( memStat.ullTotalPhys > 0xFFFFFFFFUL )
+ {
+ host_parms.memsize = 0xFFFFFFFFUL;
+ }
+ else
+ {
+ host_parms.memsize = memStat.ullTotalPhys;
+ }
+ }
+ }
+ }
+ }
+#endif // (_MSC_VER > 1200)
+
+ if ( !IsX360() )
+ {
+ if ( host_parms.memsize == 0 )
+ {
+ MEMORYSTATUS lpBuffer;
+ // Get OS Memory status
+ lpBuffer.dwLength = sizeof(MEMORYSTATUS);
+ GlobalMemoryStatus( &lpBuffer );
+
+ if ( lpBuffer.dwTotalPhys <= 0 )
+ {
+ host_parms.memsize = MAXIMUM_WIN_MEMORY;
+ }
+ else
+ {
+ host_parms.memsize = lpBuffer.dwTotalPhys;
+ }
+ }
+ if ( host_parms.memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
+ {
+ Sys_Error( "Available memory less than 128MB!!! %i\n", host_parms.memsize );
+ }
+
+ // take one quarter the physical memory
+ if ( host_parms.memsize <= 512*1024*1024)
+ {
+ host_parms.memsize >>= 2;
+ // Apply cap of 64MB for 512MB systems
+ // this keeps the code the same as HL2 gold
+ // but allows us to use more memory on 1GB+ systems
+ if (host_parms.memsize > MAXIMUM_DEDICATED_MEMORY)
+ {
+ host_parms.memsize = MAXIMUM_DEDICATED_MEMORY;
+ }
+ }
+ else
+ {
+ // just take one quarter, no cap
+ host_parms.memsize >>= 2;
+ }
+
+ // At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
+ if (host_parms.memsize < MINIMUM_WIN_MEMORY)
+ {
+ host_parms.memsize = MINIMUM_WIN_MEMORY;
+ }
+
+ // Apply cap
+ if (host_parms.memsize > MAXIMUM_WIN_MEMORY)
+ {
+ host_parms.memsize = MAXIMUM_WIN_MEMORY;
+ }
+ }
+ else
+ {
+ host_parms.memsize = 128*1024*1024;
+ }
+#elif defined(POSIX)
+ uint64_t memsize = ONE_HUNDRED_TWENTY_EIGHT_MB;
+
+#if defined(OSX)
+ int mib[2] = { CTL_HW, HW_MEMSIZE };
+ u_int namelen = sizeof(mib) / sizeof(mib[0]);
+ size_t len = sizeof(memsize);
+
+ if (sysctl(mib, namelen, &memsize, &len, NULL, 0) < 0)
+ {
+ memsize = ONE_HUNDRED_TWENTY_EIGHT_MB;
+ }
+#elif defined(LINUX)
+ const int fd = open("/proc/meminfo", O_RDONLY);
+ if (fd < 0)
+ {
+ Sys_Error( "Can't open /proc/meminfo (%s)!\n", strerror(errno) );
+ }
+
+ char buf[1024 * 16];
+ const ssize_t br = read(fd, buf, sizeof (buf));
+ close(fd);
+ if (br < 0)
+ {
+ Sys_Error( "Can't read /proc/meminfo (%s)!\n", strerror(errno) );
+ }
+ buf[br] = '\0';
+
+ // Split up the buffer by lines...
+ char *line = buf;
+ for (char *ptr = buf; *ptr; ptr++)
+ {
+ if (*ptr == '\n')
+ {
+ // we've got a complete line.
+ *ptr = '\0';
+ unsigned long long ull = 0;
+ if (sscanf(line, "MemTotal: %llu kB", &ull) == 1)
+ {
+ // found it!
+ memsize = ((uint64_t) ull) * 1024;
+ break;
+ }
+ line = ptr;
+ }
+ }
+
+#else
+#error Write me.
+#endif
+
+ if ( memsize > 0xFFFFFFFFUL )
+ {
+ host_parms.memsize = 0xFFFFFFFFUL;
+ }
+ else
+ {
+ host_parms.memsize = memsize;
+ }
+
+ if ( host_parms.memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
+ {
+ Sys_Error( "Available memory less than 128MB!!! %i\n", host_parms.memsize );
+ }
+
+ // take one quarter the physical memory
+ if ( host_parms.memsize <= 512*1024*1024)
+ {
+ host_parms.memsize >>= 2;
+ // Apply cap of 64MB for 512MB systems
+ // this keeps the code the same as HL2 gold
+ // but allows us to use more memory on 1GB+ systems
+ if (host_parms.memsize > MAXIMUM_DEDICATED_MEMORY)
+ {
+ host_parms.memsize = MAXIMUM_DEDICATED_MEMORY;
+ }
+ }
+ else
+ {
+ // just take one quarter, no cap
+ host_parms.memsize >>= 2;
+ }
+
+ // At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
+ if (host_parms.memsize < MINIMUM_WIN_MEMORY)
+ {
+ host_parms.memsize = MINIMUM_WIN_MEMORY;
+ }
+
+ // Apply cap
+ if (host_parms.memsize > MAXIMUM_WIN_MEMORY)
+ {
+ host_parms.memsize = MAXIMUM_WIN_MEMORY;
+ }
+
+#else
+#error Write me.
+
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *parms -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+void Sys_ShutdownMemory( void )
+{
+ host_parms.memsize = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Sys_InitAuthentication( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Sys_ShutdownAuthentication( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Debug library spew output
+//-----------------------------------------------------------------------------
+CThreadLocalInt<> g_bInSpew;
+
+#include "tier1/fmtstr.h"
+
+static ConVar sys_minidumpspewlines( "sys_minidumpspewlines", "500", 0, "Lines of crash dump console spew to keep." );
+
+static CUtlLinkedList< CUtlString > g_SpewHistory;
+static int g_nSpewLines = 1;
+static CThreadFastMutex g_SpewMutex;
+
+static void AddSpewRecord( char const *pMsg )
+{
+#if !defined( _X360 )
+ AUTO_LOCK( g_SpewMutex );
+
+ static bool s_bReentrancyGuard = false;
+ if ( s_bReentrancyGuard )
+ return;
+ s_bReentrancyGuard = true;
+
+ if ( g_SpewHistory.Count() > sys_minidumpspewlines.GetInt() )
+ {
+ g_SpewHistory.Remove( g_SpewHistory.Head() );
+ }
+
+ int i = g_SpewHistory.AddToTail();
+ g_SpewHistory[ i ].Format( "%d(%f): %s", g_nSpewLines++, Plat_FloatTime(), pMsg );
+
+ s_bReentrancyGuard = false;
+#endif
+}
+
+void GetSpew( char *buf, size_t buflen )
+{
+ AUTO_LOCK( g_SpewMutex );
+
+ // Walk list backward
+ char *pcur = buf;
+ int remainder = (int)buflen - 1;
+
+ // Walk backward(
+ for ( int i = g_SpewHistory.Tail(); i != g_SpewHistory.InvalidIndex(); i = g_SpewHistory.Previous( i ) )
+ {
+ const CUtlString &rec = g_SpewHistory[ i ];
+ int len = rec.Length();
+ int tocopy = MIN( len, remainder );
+
+ if ( tocopy <= 0 )
+ break;
+
+ Q_memcpy( pcur, rec.String(), tocopy );
+ remainder -= tocopy;
+ pcur += tocopy;
+
+ if ( remainder <= 0 )
+ break;
+ }
+ *pcur = 0;
+}
+
+ConVar spew_consolelog_to_debugstring( "spew_consolelog_to_debugstring", "0", 0, "Send console log to PLAT_DebugString()" );
+
+SpewRetval_t Sys_SpewFunc( SpewType_t spewType, const char *pMsg )
+{
+ bool suppress = g_bInSpew;
+
+ g_bInSpew = true;
+
+ AddSpewRecord( pMsg );
+
+ // Text output shows up on dedicated server profiles, both as consuming CPU
+ // time and causing IPC delays. Sending the messages to ETW will help us
+ // understand why, and save us time when server operators are triggering
+ // excessive spew. Having the output in traces is also generically useful
+ // for understanding slowdowns.
+ ETWMark1I( pMsg, spewType );
+
+ if ( !suppress )
+ {
+ // If this is a dedicated server, then we have taken over its spew function, but we still
+ // want its vgui console to show the spew, so pass it into the dedicated server.
+ if ( dedicated )
+ dedicated->Sys_Printf( (char*)pMsg );
+
+ if( spew_consolelog_to_debugstring.GetBool() )
+ {
+ Plat_DebugString( pMsg );
+ }
+
+ if ( g_bTextMode )
+ {
+ printf( "%s", pMsg );
+ }
+
+ if ((spewType != SPEW_LOG) || (sv.GetMaxClients() == 1))
+ {
+ Color color;
+ switch ( spewType )
+ {
+#ifndef SWDS
+ case SPEW_WARNING:
+ {
+ color.SetColor( 255, 90, 90, 255 );
+ }
+ break;
+ case SPEW_ASSERT:
+ {
+ color.SetColor( 255, 20, 20, 255 );
+ }
+ break;
+ case SPEW_ERROR:
+ {
+ color.SetColor( 20, 70, 255, 255 );
+ }
+ break;
+#endif
+ default:
+ {
+ color = *GetSpewOutputColor();
+ }
+ break;
+ }
+ Con_ColorPrintf( color, "%s", pMsg );
+
+ }
+ else
+ {
+ g_Log.Printf( "%s", pMsg );
+ }
+ }
+
+ g_bInSpew = false;
+
+ if (spewType == SPEW_ERROR)
+ {
+ Sys_Error( "%s", pMsg );
+ return SPEW_ABORT;
+ }
+ if (spewType == SPEW_ASSERT)
+ {
+ if ( CommandLine()->FindParm( "-noassert" ) == 0 )
+ return SPEW_DEBUGGER;
+ else
+ return SPEW_CONTINUE;
+ }
+ return SPEW_CONTINUE;
+}
+
+void DeveloperChangeCallback( IConVar *pConVar, const char *pOldString, float flOldValue )
+{
+ // Set the "developer" spew group to the value...
+ ConVarRef var( pConVar );
+ int val = var.GetInt();
+ SpewActivate( "developer", val );
+
+ // Activate console spew (spew value 2 == developer console spew)
+ SpewActivate( "console", val ? 2 : 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: factory comglomerator, gets the client, server, and gameui dlls together
+//-----------------------------------------------------------------------------
+void *GameFactory( const char *pName, int *pReturnCode )
+{
+ void *pRetVal = NULL;
+
+ // first ask the app factory
+ pRetVal = g_AppSystemFactory( pName, pReturnCode );
+ if (pRetVal)
+ return pRetVal;
+
+#ifndef SWDS
+ // now ask the client dll
+ if (ClientDLL_GetFactory())
+ {
+ pRetVal = ClientDLL_GetFactory()( pName, pReturnCode );
+ if (pRetVal)
+ return pRetVal;
+ }
+
+ // gameui.dll
+ if (EngineVGui()->GetGameUIFactory())
+ {
+ pRetVal = EngineVGui()->GetGameUIFactory()( pName, pReturnCode );
+ if (pRetVal)
+ return pRetVal;
+ }
+#endif
+ // server dll factory access would go here when needed
+
+ return NULL;
+}
+
+// factory instance
+CreateInterfaceFn g_GameSystemFactory = GameFactory;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *lpOrgCmdLine -
+// launcherFactory -
+// *pwnd -
+// bIsDedicated -
+// Output : int
+//-----------------------------------------------------------------------------
+int Sys_InitGame( CreateInterfaceFn appSystemFactory, const char* pBaseDir, void *pwnd, int bIsDedicated )
+{
+#ifdef BENCHMARK
+ if ( bIsDedicated )
+ {
+ Error( "Dedicated server isn't supported by this benchmark!" );
+ }
+#endif
+
+ extern void InitMathlib( void );
+ InitMathlib();
+
+ FileSystem_SetWhitelistSpewFlags();
+
+ // Activate console spew
+ // Must happen before developer.InstallChangeCallback because that callback may reset it
+ SpewActivate( "console", 1 );
+
+ // Install debug spew output....
+ developer.InstallChangeCallback( DeveloperChangeCallback );
+
+ SpewOutputFunc( Sys_SpewFunc );
+
+ // Assume failure
+ host_initialized = false;
+
+#ifdef PLATFORM_WINDOWS
+ // Grab main window pointer
+ pmainwindow = (HWND *)pwnd;
+#endif
+
+ // Remember that this is a dedicated server
+ s_bIsDedicated = bIsDedicated ? true : false;
+
+ memset( &gmodinfo, 0, sizeof( modinfo_t ) );
+
+ static char s_pBaseDir[256];
+ Q_strncpy( s_pBaseDir, pBaseDir, sizeof( s_pBaseDir ) );
+ Q_strlower( s_pBaseDir );
+ Q_FixSlashes( s_pBaseDir );
+ host_parms.basedir = s_pBaseDir;
+
+#ifndef _X360
+ if ( CommandLine()->FindParm ( "-pidfile" ) )
+ {
+ FileHandle_t pidFile = g_pFileSystem->Open( CommandLine()->ParmValue ( "-pidfile", "srcds.pid" ), "w+" );
+ if ( pidFile )
+ {
+ g_pFileSystem->FPrintf( pidFile, "%i\n", getpid() );
+ g_pFileSystem->Close(pidFile);
+ }
+ else
+ {
+ Warning("Unable to open pidfile (%s)\n", CommandLine()->CheckParm ( "-pidfile" ));
+ }
+ }
+#endif
+
+ // Initialize clock
+ TRACEINIT( Sys_Init(), Sys_Shutdown() );
+
+#if defined(_DEBUG)
+ if ( IsPC() )
+ {
+ if( !CommandLine()->FindParm( "-nodttest" ) && !CommandLine()->FindParm( "-dti" ) )
+ {
+ RunDataTableTest();
+ }
+ }
+#endif
+
+ // NOTE: Can't use COM_CheckParm here because it hasn't been set up yet.
+ SeedRandomNumberGenerator( CommandLine()->FindParm( "-random_invariant" ) != 0 );
+
+ TRACEINIT( Sys_InitMemory(), Sys_ShutdownMemory() );
+
+ TRACEINIT( Host_Init( s_bIsDedicated ), Host_Shutdown() );
+
+ if ( !host_initialized )
+ {
+ return 0;
+ }
+
+ TRACEINIT( Sys_InitAuthentication(), Sys_ShutdownAuthentication() );
+
+ MapReslistGenerator_BuildMapList();
+
+ BuildMinidumpComment( NULL, false );
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Sys_ShutdownGame( void )
+{
+ TRACESHUTDOWN( Sys_ShutdownAuthentication() );
+
+ TRACESHUTDOWN( Host_Shutdown() );
+
+ TRACESHUTDOWN( Sys_ShutdownMemory() );
+
+ // TRACESHUTDOWN( Sys_ShutdownArgv() );
+
+ TRACESHUTDOWN( Sys_Shutdown() );
+
+ // Remove debug spew output....
+ developer.InstallChangeCallback( 0 );
+ SpewOutputFunc( 0 );
+}
+
+//
+// Try to load a single DLL. If it conforms to spec, keep it loaded, and add relevant
+// info to the DLL directory. If not, ignore it entirely.
+//
+
+CreateInterfaceFn g_ServerFactory;
+
+
+#pragma optimize( "g", off )
+static bool LoadThisDll( char *szDllFilename, bool bIsServerOnly )
+{
+ CSysModule *pDLL = NULL;
+
+ // check signature, don't let users with modified binaries connect to secure servers, they will get VAC banned
+ if ( !Host_AllowLoadModule( szDllFilename, "GAMEBIN", true, bIsServerOnly ) )
+ {
+ // not supposed to load this but we will anyway
+ Host_DisallowSecureServers();
+ Host_AllowLoadModule( szDllFilename, "GAMEBIN", true, bIsServerOnly );
+ }
+ // Load DLL, ignore if cannot
+ // ensures that the game.dll is running under Steam
+ // this will have to be undone when we want mods to be able to run
+ if ((pDLL = g_pFileSystem->LoadModule(szDllFilename, "GAMEBIN", false)) == NULL)
+ {
+ ConMsg("Failed to load %s\n", szDllFilename);
+ goto IgnoreThisDLL;
+ }
+
+ // Load interface factory and any interfaces exported by the game .dll
+ g_iServerGameDLLVersion = 0;
+ g_ServerFactory = Sys_GetFactory( pDLL );
+ if ( g_ServerFactory )
+ {
+ // Figure out latest version we understand
+ g_iServerGameDLLVersion = INTERFACEVERSION_SERVERGAMEDLL_INT;
+
+ // Scan for most recent version the game DLL understands.
+ for (;;)
+ {
+ char archVersion[64];
+ V_sprintf_safe( archVersion, "ServerGameDLL%03d", g_iServerGameDLLVersion );
+ serverGameDLL = (IServerGameDLL*)g_ServerFactory(archVersion, NULL);
+ if ( serverGameDLL )
+ break;
+ --g_iServerGameDLLVersion;
+ if ( g_iServerGameDLLVersion < 4 )
+ {
+ g_iServerGameDLLVersion = 0;
+ Msg( "Could not get IServerGameDLL interface from library %s", szDllFilename );
+ goto IgnoreThisDLL;
+ }
+ }
+
+ serverGameEnts = (IServerGameEnts*)g_ServerFactory(INTERFACEVERSION_SERVERGAMEENTS, NULL);
+ if ( !serverGameEnts )
+ {
+ ConMsg( "Could not get IServerGameEnts interface from library %s", szDllFilename );
+ goto IgnoreThisDLL;
+ }
+
+ serverGameClients = (IServerGameClients*)g_ServerFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL);
+ if ( serverGameClients )
+ {
+ g_iServerGameClientsVersion = 4;
+ }
+ else
+ {
+ // Try the previous version.
+ const char *pINTERFACEVERSION_SERVERGAMECLIENTS_V3 = "ServerGameClients003";
+ serverGameClients = (IServerGameClients*)g_ServerFactory(pINTERFACEVERSION_SERVERGAMECLIENTS_V3, NULL);
+ if ( serverGameClients )
+ {
+ g_iServerGameClientsVersion = 3;
+ }
+ else
+ {
+ ConMsg( "Could not get IServerGameClients interface from library %s", szDllFilename );
+ goto IgnoreThisDLL;
+ }
+ }
+ serverGameDirector = (IHLTVDirector*)g_ServerFactory(INTERFACEVERSION_HLTVDIRECTOR, NULL);
+ if ( !serverGameDirector )
+ {
+ ConMsg( "Could not get IHLTVDirector interface from library %s", szDllFilename );
+ // this is not a critical
+ }
+
+ serverGameTags = (IServerGameTags*)g_ServerFactory(INTERFACEVERSION_SERVERGAMETAGS, NULL);
+ // Possible that this is NULL - optional interface
+ }
+ else
+ {
+ ConMsg( "Could not find factory interface in library %s", szDllFilename );
+ goto IgnoreThisDLL;
+ }
+
+ g_GameDLL = pDLL;
+ return true;
+
+IgnoreThisDLL:
+ if (pDLL != NULL)
+ {
+ g_pFileSystem->UnloadModule(pDLL);
+ serverGameDLL = NULL;
+ serverGameEnts = NULL;
+ serverGameClients = NULL;
+ }
+ return false;
+}
+#pragma optimize( "", on )
+
+//
+// Scan DLL directory, load all DLLs that conform to spec.
+//
+void LoadEntityDLLs( const char *szBaseDir, bool bIsServerOnly )
+{
+ memset( &gmodinfo, 0, sizeof( modinfo_t ) );
+ gmodinfo.version = 1;
+ gmodinfo.svonly = true;
+
+ // Run through all DLLs found in the extension DLL directory
+ g_GameDLL = NULL;
+ sv_noclipduringpause = NULL;
+
+ // Listing file for this game.
+ KeyValues *modinfo = new KeyValues("modinfo");
+ MEM_ALLOC_CREDIT();
+ if (modinfo->LoadFromFile(g_pFileSystem, "gameinfo.txt"))
+ {
+ Q_strncpy( gmodinfo.szInfo, modinfo->GetString("url_info"), sizeof( gmodinfo.szInfo ) );
+ Q_strncpy( gmodinfo.szDL, modinfo->GetString("url_dl"), sizeof( gmodinfo.szDL ) );
+ gmodinfo.version = modinfo->GetInt("version");
+ gmodinfo.size = modinfo->GetInt("size");
+ gmodinfo.svonly = modinfo->GetInt("svonly") ? true : false;
+ gmodinfo.cldll = modinfo->GetInt("cldll") ? true : false;
+ Q_strncpy( gmodinfo.szHLVersion, modinfo->GetString("hlversion"), sizeof( gmodinfo.szHLVersion ) );
+ }
+ modinfo->deleteThis();
+
+ // Load the game .dll
+ LoadThisDll( "server" DLL_EXT_STRING, bIsServerOnly );
+
+ if ( serverGameDLL )
+ {
+ Msg("server%s loaded for \"%s\"\n", DLL_EXT_STRING, (char *)serverGameDLL->GetGameDescription());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieves a string value from the registry
+//-----------------------------------------------------------------------------
+#if defined(_WIN32)
+void Sys_GetRegKeyValueUnderRoot( HKEY rootKey, const char *pszSubKey, const char *pszElement, OUT_Z_CAP(nReturnLength) char *pszReturnString, int nReturnLength, const char *pszDefaultValue )
+{
+ LONG lResult; // Registry function result code
+ HKEY hKey; // Handle of opened/created key
+ char szBuff[128]; // Temp. buffer
+ ULONG dwDisposition; // Type of key opening event
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ // Copying a string to itself is both unnecessary and illegal.
+ // Address sanitizer prohibits this so we have to fix this in order
+ // to continue testing with it.
+ if ( pszReturnString != pszDefaultValue )
+ {
+ // Assume the worst
+ Q_strncpy(pszReturnString, pszDefaultValue, nReturnLength );
+ }
+
+ // Create it if it doesn't exist. (Create opens the key otherwise)
+ lResult = VCRHook_RegCreateKeyEx(
+ rootKey, // handle of open key
+ pszSubKey, // address of name of subkey to open
+ 0ul, // DWORD ulOptions, // reserved
+ "String", // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ &hKey, // Key we are creating
+ &dwDisposition); // Type of creation
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return;
+
+ // First time, just set to Valve default
+ if (dwDisposition == REG_CREATED_NEW_KEY)
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszDefaultValue, Q_strlen(pszDefaultValue) + 1 );
+ }
+ else
+ {
+ // We opened the existing key. Now go ahead and find out how big the key is.
+ dwSize = nReturnLength;
+ lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)szBuff, &dwSize );
+
+ // Success?
+ if (lResult == ERROR_SUCCESS)
+ {
+ // Only copy strings, and only copy as much data as requested.
+ if (dwType == REG_SZ)
+ {
+ Q_strncpy(pszReturnString, szBuff, nReturnLength);
+ pszReturnString[nReturnLength - 1] = '\0';
+ }
+ }
+ else
+ // Didn't find it, so write out new value
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszDefaultValue, Q_strlen(pszDefaultValue) + 1 );
+ }
+ };
+
+ // Always close this key before exiting.
+ VCRHook_RegCloseKey(hKey);
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieves a DWORD value from the registry
+//-----------------------------------------------------------------------------
+void Sys_GetRegKeyValueUnderRootInt( HKEY rootKey, const char *pszSubKey, const char *pszElement, long *plReturnValue, const long lDefaultValue )
+{
+ LONG lResult; // Registry function result code
+ HKEY hKey; // Handle of opened/created key
+ ULONG dwDisposition; // Type of key opening event
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ // Assume the worst
+ // Set the return value to the default
+ *plReturnValue = lDefaultValue;
+
+ // Create it if it doesn't exist. (Create opens the key otherwise)
+ lResult = VCRHook_RegCreateKeyEx(
+ rootKey, // handle of open key
+ pszSubKey, // address of name of subkey to open
+ 0ul, // DWORD ulOptions, // reserved
+ "String", // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ &hKey, // Key we are creating
+ &dwDisposition); // Type of creation
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return;
+
+ // First time, just set to Valve default
+ if (dwDisposition == REG_CREATED_NEW_KEY)
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_DWORD, (CONST BYTE *)&lDefaultValue, sizeof( DWORD ) );
+ }
+ else
+ {
+ // We opened the existing key. Now go ahead and find out how big the key is.
+ dwSize = sizeof( DWORD );
+ lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)plReturnValue, &dwSize );
+
+ // Success?
+ if (lResult != ERROR_SUCCESS)
+ // Didn't find it, so write out new value
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_DWORD, (LPBYTE)&lDefaultValue, sizeof( DWORD ) );
+ }
+ };
+
+ // Always close this key before exiting.
+ VCRHook_RegCloseKey(hKey);
+
+}
+
+
+void Sys_SetRegKeyValueUnderRoot( HKEY rootKey, const char *pszSubKey, const char *pszElement, const char *pszValue )
+{
+ LONG lResult; // Registry function result code
+ HKEY hKey; // Handle of opened/created key
+ //char szBuff[128]; // Temp. buffer
+ ULONG dwDisposition; // Type of key opening event
+ //DWORD dwType; // Type of key
+ //DWORD dwSize; // Size of element data
+
+ // Create it if it doesn't exist. (Create opens the key otherwise)
+ lResult = VCRHook_RegCreateKeyEx(
+ rootKey, // handle of open key
+ pszSubKey, // address of name of subkey to open
+ 0ul, // DWORD ulOptions, // reserved
+ "String", // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ &hKey, // Key we are creating
+ &dwDisposition); // Type of creation
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return;
+
+ // First time, just set to Valve default
+ if (dwDisposition == REG_CREATED_NEW_KEY)
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszValue, Q_strlen(pszValue) + 1 );
+ }
+ else
+ {
+ /*
+ // FIXE: We might want to support a mode where we only create this key, we don't overwrite values already present
+ // We opened the existing key. Now go ahead and find out how big the key is.
+ dwSize = nReturnLength;
+ lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)szBuff, &dwSize );
+
+ // Success?
+ if (lResult == ERROR_SUCCESS)
+ {
+ // Only copy strings, and only copy as much data as requested.
+ if (dwType == REG_SZ)
+ {
+ Q_strncpy(pszReturnString, szBuff, nReturnLength);
+ pszReturnString[nReturnLength - 1] = '\0';
+ }
+ }
+ else
+ */
+ // Didn't find it, so write out new value
+ {
+ // Just Set the Values according to the defaults
+ lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszValue, Q_strlen(pszValue) + 1 );
+ }
+ };
+
+ // Always close this key before exiting.
+ VCRHook_RegCloseKey(hKey);
+}
+#endif
+
+void Sys_GetRegKeyValue( const char *pszSubKey, const char *pszElement, OUT_Z_CAP(nReturnLength) char *pszReturnString, int nReturnLength, const char *pszDefaultValue )
+{
+#if defined(_WIN32)
+ Sys_GetRegKeyValueUnderRoot( HKEY_CURRENT_USER, pszSubKey, pszElement, pszReturnString, nReturnLength, pszDefaultValue );
+#else
+ //hushed Assert( !"Impl me" );
+ // Copying a string to itself is both unnecessary and illegal.
+ if ( pszReturnString != pszDefaultValue )
+ {
+ Q_strncpy( pszReturnString, pszDefaultValue, nReturnLength );
+ }
+#endif
+}
+
+void Sys_GetRegKeyValueInt( const char *pszSubKey, const char *pszElement, long *plReturnValue, long lDefaultValue)
+{
+#if defined(_WIN32)
+ Sys_GetRegKeyValueUnderRootInt( HKEY_CURRENT_USER, pszSubKey, pszElement, plReturnValue, lDefaultValue );
+#else
+ //hushed Assert( !"Impl me" );
+ *plReturnValue = lDefaultValue;
+#endif
+}
+
+void Sys_SetRegKeyValue( const char *pszSubKey, const char *pszElement, const char *pszValue )
+{
+#if defined(_WIN32)
+ Sys_SetRegKeyValueUnderRoot( HKEY_CURRENT_USER, pszSubKey, pszElement, pszValue );
+#else
+ //hushed Assert( !"Impl me" );
+#endif
+}
+
+#define SOURCE_ENGINE_APP_CLASS "Valve.Source"
+
+void Sys_CreateFileAssociations( int count, FileAssociationInfo *list )
+{
+#if defined(_WIN32)
+ if ( IsX360() )
+ return;
+
+ char appname[ 512 ];
+
+ GetModuleFileName( 0, appname, sizeof( appname ) );
+ Q_FixSlashes( appname );
+ Q_strlower( appname );
+
+ char quoted_appname_with_arg[ 512 ];
+ Q_snprintf( quoted_appname_with_arg, sizeof( quoted_appname_with_arg ), "\"%s\" \"%%1\"", appname );
+ char base_exe_name[ 256 ];
+ Q_FileBase( appname, base_exe_name, sizeof( base_exe_name) );
+ Q_DefaultExtension( base_exe_name, ".exe", sizeof( base_exe_name ) );
+
+ // HKEY_CLASSES_ROOT/Valve.Source/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
+ Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, va( "%s\\shell\\open\\command", SOURCE_ENGINE_APP_CLASS ), "", quoted_appname_with_arg );
+ // HKEY_CLASSES_ROOT/Applications/hl2.exe/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
+ Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, va( "Applications\\%s\\shell\\open\\command", base_exe_name ), "", quoted_appname_with_arg );
+
+ for ( int i = 0; i < count ; i++ )
+ {
+ FileAssociationInfo *fa = &list[ i ];
+ char binding[32];
+ binding[0] = 0;
+ // Create file association for our .exe
+ // HKEY_CLASSES_ROOT/.dem == "Valve.Source"
+ Sys_GetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, fa->extension, "", binding, sizeof(binding), "" );
+ if ( Q_strlen( binding ) == 0 )
+ {
+ Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, fa->extension, "", SOURCE_ENGINE_APP_CLASS );
+ }
+ }
+#endif
+}
+
+void Sys_NoCrashDialog()
+{
+#if defined(_WIN32)
+ ::SetErrorMode(SetErrorMode(SEM_NOGPFAULTERRORBOX) | SEM_NOGPFAULTERRORBOX);
+#endif
+}
+
+void Sys_TestSendKey( const char *pKey )
+{
+#if defined(_WIN32) && !defined(USE_SDL) && !defined(_XBOX)
+ int key = pKey[0];
+ if ( pKey[0] == '\\' && pKey[1] == 'r' )
+ {
+ key = VK_RETURN;
+ }
+
+ HWND hWnd = (HWND)game->GetMainWindow();
+ PostMessageA( hWnd, WM_KEYDOWN, key, 0 );
+ PostMessageA( hWnd, WM_KEYUP, key, 0 );
+
+ //void Key_Event (int key, bool down);
+ //Key_Event( key, 1 );
+ //Key_Event( key, 0 );
+#endif
+}
+
+void Sys_OutputDebugString(const char *msg)
+{
+ Plat_DebugString( msg );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void UnloadEntityDLLs( void )
+{
+ if ( !g_GameDLL )
+ return;
+
+ // Unlink the cvars associated with game DLL
+ FileSystem_UnloadModule( g_GameDLL );
+ g_GameDLL = NULL;
+ serverGameDLL = NULL;
+ serverGameEnts = NULL;
+ serverGameClients = NULL;
+ sv_noclipduringpause = NULL;
+}
+
+CON_COMMAND( star_memory, "Dump memory stats" )
+{
+ // get a current stat of available memory
+ // 32 MB is reserved and fixed by OS, so not reporting to allow memory loggers sync
+#ifdef LINUX
+ struct mallinfo memstats = mallinfo( );
+ Msg( "sbrk size: %.2f MB, Used: %.2f MB, #mallocs = %d\n",
+ memstats.arena / ( 1024.0 * 1024.0), memstats.uordblks / ( 1024.0 * 1024.0 ), memstats.hblks );
+#elif OSX
+ struct mstats memstats = mstats( );
+ Msg( "Available %.2f MB, Used: %.2f MB, #mallocs = %lu\n",
+ memstats.bytes_free / ( 1024.0 * 1024.0), memstats.bytes_used / ( 1024.0 * 1024.0 ), memstats.chunks_used );
+#else
+ MEMORYSTATUS stat;
+ GlobalMemoryStatus( &stat );
+ Msg( "Available: %.2f MB, Used: %.2f MB, Free: %.2f MB\n",
+ stat.dwTotalPhys/( 1024.0f*1024.0f ) - 32.0f,
+ ( stat.dwTotalPhys - stat.dwAvailPhys )/( 1024.0f*1024.0f ) - 32.0f,
+ stat.dwAvailPhys/( 1024.0f*1024.0f ) );
+#endif
+}