diff --git a/game/shared/choreoscene.cpp b/game/shared/choreoscene.cpp index 060b1697..142fbe3c 100644 --- a/game/shared/choreoscene.cpp +++ b/game/shared/choreoscene.cpp @@ -102,7 +102,7 @@ void CChoreoScene::choreoprintf( int level, const char *fmt, ... ) } else { - printf( string ); + printf( "%s", string ); } Msg( "%s", string ); diff --git a/lib/linux/tier1_i486.a b/lib/linux/tier1_i486.a index 8c7a7979..7d1afed0 100644 Binary files a/lib/linux/tier1_i486.a and b/lib/linux/tier1_i486.a differ diff --git a/lib/mac/tier1_i486.a b/lib/mac/tier1_i486.a index 74c8aca5..d43d4799 100644 Binary files a/lib/mac/tier1_i486.a and b/lib/mac/tier1_i486.a differ diff --git a/lib/public/tier1.lib b/lib/public/tier1.lib index b6d359d0..e45eed09 100644 Binary files a/lib/public/tier1.lib and b/lib/public/tier1.lib differ diff --git a/linux_sdk/Makefile b/linux_sdk/Makefile index ac5b553d..4ab8d897 100644 --- a/linux_sdk/Makefile +++ b/linux_sdk/Makefile @@ -35,7 +35,7 @@ else CC = /usr/bin/gcc CPLUS = /usr/bin/g++ CLINK = /usr/bin/gcc -CPP_LIB = $(SRCDS_DIR)/bin/libstdc++.so.6 $(SRCDS_DIR)/bin/libgcc_s.so.1 +CPP_LIB = #$(SRCDS_DIR)/bin/libstdc++.so.6 $(SRCDS_DIR)/bin/libgcc_s.so.1 endif # put any compiler flags you want passed here @@ -57,6 +57,16 @@ DEBUG = false ############################################################################# # Things below here shouldn't need to be altered ############################################################################# +IS_CLANG := $(shell $(CPP) --version | head -1 | grep clang > /dev/null && echo "1" || echo "0") + +ifeq "$(IS_CLANG)" "1" +CPP_MAJOR := $(shell $(CPP) --version | grep clang | sed "s/.*version \([0-9]\)*\.[0-9]*.*/\1/") +CPP_MINOR := $(shell $(CPP) --version | grep clang | sed "s/.*version [0-9]*\.\([0-9]\)*.*/\1/") +else +CPP_MAJOR := $(shell $(CPP) -dumpversion >&1 | cut -b1) +CPP_MINOR := $(shell $(CPP) -dumpversion >&1 | cut -b3) +endif + MAKE = make AR = "ar rvs" @@ -77,28 +87,38 @@ endif # the CPU target for the build, must be i486 for now ARCH = i486 -ARCH_CFLAGS = -mtune=i686 -march=pentium3 -mmmx -m32 +ARCH_CFLAGS = -mtune=i686 -march=pentium3 -mmmx -msse -msse2 -m32 ifeq "$(OS)" "Darwin" -DEFINES = -D_OSX -DOSX +DEFINES = -D_OSX -DOSX -D_DLL_EXT=.dylib +ARCH_CFLAGS += -mmacosx-version-min=10.5 SHLIBEXT = dylib SHLIBLDFLAGS = -dynamiclib -mmacosx-version-min=10.5 SHLIBSUFFIX = else -DEFINES = -D_LINUX -DLINUX +DEFINES = -D_LINUX -DLINUX -D_DLL_EXT=.so SHLIBEXT = so -SHLIBLDFLAGS = -shared -Wl,-Map,$@_map.txt -Wl +SHLIBLDFLAGS = -shared -Wl,-Map,$@_map.txt SHLIBSUFFIX = _srv endif DEFINES +=-DVPROF_LEVEL=1 -DSWDS -D_finite=finite -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp \ - -Dstrnicmp=strncasecmp -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp + -Dstrnicmp=strncasecmp -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -DPOSIX -DGNUC -DCOMPILER_GCC -DNO_MALLOC_OVERRIDE UNDEF = -Usprintf -Ustrncpy -UPROTECTED_THINGS_ENABLE BASE_CFLAGS = -fno-strict-aliasing -Wall -Wsign-compare -Werror -Wno-conversion -Wno-overloaded-virtual -Wno-non-virtual-dtor -Wno-invalid-offsetof \ - -Wno-delete-non-virtual-dtor + -Wno-unknown-pragmas -Wno-unused SHLIBCFLAGS = -fPIC +# Clang >= 3 || GCC >= 4.7 +ifeq "$(shell expr $(IS_CLANG) \& $(CPP_MAJOR) \>= 3 \| $(CPP_MAJOR) \>= 4 \& $(CPP_MINOR) \>= 7)" "1" +BASE_CFLAGS += -Wno-delete-non-virtual-dtor -Wno-narrowing +endif + +ifeq "$(shell expr $(IS_CLANG) \= 0 \& $(CPP_MAJOR) \>= 6)" "1" +BASE_CFLAGS += -Wno-nonnull-compare +endif + # Flags passed to the c compiler CFLAGS = $(DEFINES) $(ARCH_CFLAGS) -O3 $(BASE_CFLAGS) ifdef USER_CFLAGS diff --git a/public/datacache/idatacache.h b/public/datacache/idatacache.h index 029ff0e1..2a56e4ba 100644 --- a/public/datacache/idatacache.h +++ b/public/datacache/idatacache.h @@ -513,12 +513,14 @@ public: case DC_AGE_DISCARD: case DC_FLUSH_DISCARD: case DC_REMOVED: - STORAGE_TYPE *p = (STORAGE_TYPE *)notification.clientId; - p->DestroyResource(); + { + STORAGE_TYPE *p = (STORAGE_TYPE *)notification.clientId; + p->DestroyResource(); + } return true; + default: + return CDefaultDataCacheClient::HandleCacheNotification( notification ); } - - return CDefaultDataCacheClient::HandleCacheNotification( notification ); } diff --git a/public/filesystem/IQueuedLoader.h b/public/filesystem/IQueuedLoader.h index 17a80119..a167cec8 100644 --- a/public/filesystem/IQueuedLoader.h +++ b/public/filesystem/IQueuedLoader.h @@ -14,6 +14,8 @@ #include "tier0/platform.h" #include "appframework/IAppSystem.h" +class CFunctor; + enum LoaderError_t { LOADERERROR_NONE = 0, @@ -30,6 +32,8 @@ enum LoaderPriority_t typedef void ( *QueuedLoaderCallback_t )( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError ); +typedef void ( *DynamicResourceCallback_t )( const char *pFilename, void *pContext, void *pContext2 ); + struct LoaderJob_t { LoaderJob_t() @@ -106,7 +110,7 @@ public: #define LOADER_DETAIL_LATECOMPLETIONS (1<<2) #define LOADER_DETAIL_PURGES (1<<3) -#define QUEUEDLOADER_INTERFACE_VERSION "QueuedLoaderVersion001" +#define QUEUEDLOADER_INTERFACE_VERSION "QueuedLoaderVersion004" abstract_class IQueuedLoader : public IAppSystem { public: @@ -122,6 +126,11 @@ public: // injects a resource into the map's reslist, rejected if not understood virtual void AddMapResource( const char *pFilename ) = 0; + // dynamically load a map resource + virtual void DynamicLoadMapResource( const char *pFilename, DynamicResourceCallback_t pCallback, void *pContext, void *pContext2 ) = 0; + virtual void QueueDynamicLoadFunctor( CFunctor* pFunctor ) = 0; + virtual bool CompleteDynamicLoad() = 0; + // callback is asynchronous virtual bool ClaimAnonymousJob( const char *pFilename, QueuedLoaderCallback_t pCallback, void *pContext, void *pContext2 = NULL ) = 0; // provides data if loaded, caller owns data @@ -134,6 +143,8 @@ public: // callers can expect that jobs are not immediately started when batching virtual bool IsBatching() const = 0; + virtual bool IsDynamic() const = 0; + // callers can conditionalize operational spew virtual int GetSpewDetail() const = 0; diff --git a/public/filesystem_init.cpp b/public/filesystem_init.cpp index 022522ad..6b97faf9 100644 --- a/public/filesystem_init.cpp +++ b/public/filesystem_init.cpp @@ -1,4 +1,4 @@ -//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. ======= +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -6,21 +6,24 @@ #undef PROTECTED_THINGS_ENABLE #undef PROTECT_FILEIO_FUNCTIONS +#ifndef POSIX +#undef fopen +#endif #if defined( _WIN32 ) && !defined( _X360 ) #include #include #include #include -#elif defined( _LINUX ) || defined( __APPLE__ ) +#elif defined( POSIX ) #include -#define _putenv putenv #define _chdir chdir #define _access access #endif #include #include #include "tier1/strtools.h" +#include "tier1/utlbuffer.h" #include "filesystem_init.h" #include "tier0/icommandline.h" #include "KeyValues.h" @@ -83,7 +86,7 @@ public: if ( pValue ) { m_bExisted = true; - m_OriginalValue.SetSize( strlen( pValue ) + 1 ); + m_OriginalValue.SetSize( Q_strlen( pValue ) + 1 ); memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() ); } else @@ -144,16 +147,24 @@ public: Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker ); va_end( marker ); +#ifdef WIN32 char str[4096]; Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString ); _putenv( str ); +#else + setenv( m_pVarName, valueString, 1 ); +#endif } void ClearValue() { +#ifdef WIN32 char str[512]; Q_snprintf( str, sizeof( str ), "%s=", m_pVarName ); _putenv( str ); +#else + setenv( m_pVarName, "", 1 ); +#endif } private: @@ -213,6 +224,7 @@ CFSSearchPathsInit::CFSSearchPathsInit() m_pDirectoryName = NULL; m_pLanguage = NULL; m_ModPath[0] = 0; + m_bMountHDContent = m_bLowViolence = false; } @@ -248,47 +260,6 @@ const char *FileSystem_GetLastErrorString() } -void AddLanguageGameDir( IFileSystem *pFileSystem, const char *pLocation, const char *pLanguage ) -{ - if ( IsX360() ) - { - // 360 does not use this path for localization - return; - } - -#if !defined( SWDS ) - char temp[MAX_PATH]; - Q_snprintf( temp, sizeof(temp), "%s_%s", pLocation, pLanguage ); - pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); - - if ( !pFileSystem->IsSteam() ) - { - // also look in "..\localization\" if not running Steam - char baseDir[MAX_PATH]; - char *tempPtr = NULL, *gameDir = NULL; - - Q_strncpy( baseDir, pLocation, sizeof(baseDir) ); - tempPtr = Q_strstr( baseDir, "\\game\\" ); - - if ( tempPtr ) - { - gameDir = tempPtr + Q_strlen( "\\game\\" ); - *tempPtr = 0; - Q_snprintf( temp, sizeof(temp), "%s%clocalization%c%s_%s", baseDir, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, gameDir, pLanguage ); - pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL ); - } - } -#endif -} - - -void AddGameBinDir( IFileSystem *pFileSystem, const char *pLocation ) -{ - char temp[MAX_PATH]; - Q_snprintf( temp, sizeof(temp), "%s%cbin", pLocation, CORRECT_PATH_SEPARATOR ); - pFileSystem->AddSearchPath( temp, "GAMEBIN", PATH_ADD_TO_TAIL ); -} - KeyValues* ReadKeyValuesFile( const char *pFilename ) { // Read in the gameinfo.txt file and null-terminate it. @@ -376,7 +347,8 @@ bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen ) Q_StrRight( exedir, 4, ext, sizeof( ext ) ); if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 ) { - Q_strncat( exedir, "\\bin", exeDirLen, COPY_ALL_CHARACTERS ); + Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS ); + Q_strncat( exedir, "bin", exeDirLen, COPY_ALL_CHARACTERS ); Q_FixSlashes( exedir ); } @@ -489,124 +461,73 @@ FSReturnCode_t LoadGameInfoFile( return FS_OK; } -// checks the registry for the low violence setting -// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3" -bool IsLowViolenceBuild( void ) -{ -#if defined(_WIN32) - HKEY hKey; - char szValue[64]; - unsigned long len = sizeof(szValue) - 1; - bool retVal = false; - - if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS ) - { - // User Token 2 - if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) - { - if ( Q_strlen( szValue ) > 0 ) - { - retVal = true; - } - } - - if ( !retVal ) - { - // reset "len" for the next check - len = sizeof(szValue) - 1; - - // User Token 3 - if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS ) - { - if ( Q_strlen( szValue ) > 0 ) - { - retVal = true; - } - } - } - - RegCloseKey(hKey); - } - - return retVal; -#elif _LINUX - return false; -#else - #error "Fix me" -#endif -} static void FileSystem_AddLoadedSearchPath( CFSSearchPathsInit &initInfo, const char *pPathID, - bool *bFirstGamePath, - const char *pBaseDir, - const char *pLocation, + const char *fullLocationPath, bool bLowViolence ) { - char fullLocationPath[MAX_PATH]; - Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation, pBaseDir ); - // Now resolve any ./'s. - V_FixSlashes( fullLocationPath ); - if ( !V_RemoveDotSlashes( fullLocationPath ) ) - Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", fullLocationPath ); - - // Add language, mod, and gamebin search paths automatically. - if ( Q_stricmp( pPathID, "game" ) == 0 ) + // Check for mounting LV game content in LV builds only + if ( V_stricmp( pPathID, "game_lv" ) == 0 ) { - // add the low violence path - if ( bLowViolence ) - { - char szPath[MAX_PATH]; - Q_snprintf( szPath, sizeof(szPath), "%s_lv", fullLocationPath ); - initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); - } - - // add the language path - if ( initInfo.m_pLanguage ) - { - AddLanguageGameDir( initInfo.m_pFileSystem, fullLocationPath, initInfo.m_pLanguage ); - } + // Not in LV build, don't mount + if ( !initInfo.m_bLowViolence ) + return; + + // Mount, as a game path + pPathID = "game"; + } + + // Check for mounting HD game content if enabled + if ( V_stricmp( pPathID, "game_hd" ) == 0 ) + { + + // Not in LV build, don't mount + if ( !initInfo.m_bMountHDContent ) + return; + + // Mount, as a game path + pPathID = "game"; + } + + + // Special processing for ordinary game folders + if ( V_stristr( fullLocationPath, ".vpk" ) == NULL && Q_stricmp( pPathID, "game" ) == 0 ) + { if ( CommandLine()->FindParm( "-tempcontent" ) != 0 ) { char szPath[MAX_PATH]; Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath ); initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); } + } - // mark the first "game" dir as the "MOD" dir - if ( *bFirstGamePath ) - { - *bFirstGamePath = false; - initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, "MOD", PATH_ADD_TO_TAIL ); - Q_strncpy( initInfo.m_ModPath, fullLocationPath, sizeof( initInfo.m_ModPath ) ); - } - // add the game bin - AddGameBinDir( initInfo.m_pFileSystem, fullLocationPath ); + if ( initInfo.m_pLanguage && + Q_stricmp( initInfo.m_pLanguage, "english" ) && + V_strstr( fullLocationPath, "_english" ) != NULL ) + { + char szPath[MAX_PATH]; + char szLangString[MAX_PATH]; + + // Need to add a language version of this path first + + Q_snprintf( szLangString, sizeof(szLangString), "_%s", initInfo.m_pLanguage); + V_StrSubst( fullLocationPath, "_english", szLangString, szPath, sizeof( szPath ), true ); + initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL ); } initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL ); } - -bool FileSystem_IsHldsUpdateToolDedicatedServer() +static int SortStricmp( char * const * sz1, char * const * sz2 ) { - // To determine this, we see if the directory our executable was launched from is "orangebox". - // We only are under "orangebox" if we're run from hldsupdatetool. - char baseDir[MAX_PATH]; - if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) - return false; - - V_FixSlashes( baseDir ); - V_StripTrailingSlash( baseDir ); - const char *pLastDir = V_UnqualifiedFileName( baseDir ); - return ( pLastDir && V_stricmp( pLastDir, "orangebox" ) == 0 ); + return V_stricmp( *sz1, *sz2 ); } - FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) { if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName ) @@ -622,23 +543,41 @@ FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); - initInfo.m_ModPath[0] = 0; + // The MOD directory is always the one that contains gameinfo.txt + Q_strncpy( initInfo.m_ModPath, initInfo.m_pDirectoryName, sizeof( initInfo.m_ModPath ) ); #define GAMEINFOPATH_TOKEN "|gameinfo_path|" #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|" - bool bLowViolence = IsLowViolenceBuild(); - bool bFirstGamePath = true; - + const char *pszExtraSearchPath = CommandLine()->ParmValue( "-insert_search_path" ); + if ( pszExtraSearchPath ) + { + CUtlStringList vecPaths; + V_SplitString( pszExtraSearchPath, ",", vecPaths ); + FOR_EACH_VEC( vecPaths, idxExtraPath ) + { + char szAbsSearchPath[MAX_PATH]; + Q_StripPrecedingAndTrailingWhitespace( vecPaths[ idxExtraPath ] ); + V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), vecPaths[ idxExtraPath ], baseDir ); + V_FixSlashes( szAbsSearchPath ); + if ( !V_RemoveDotSlashes( szAbsSearchPath ) ) + Error( "Bad -insert_search_path - Can't resolve pathname for '%s'", szAbsSearchPath ); + V_StripTrailingSlash( szAbsSearchPath ); + FileSystem_AddLoadedSearchPath( initInfo, "GAME", szAbsSearchPath, false ); + FileSystem_AddLoadedSearchPath( initInfo, "MOD", szAbsSearchPath, false ); + } + } + + bool bLowViolence = initInfo.m_bLowViolence; for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) { - const char *pPathID = pCur->GetName(); const char *pLocation = pCur->GetString(); - + const char *pszBaseDir = baseDir; + if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation ) { pLocation += strlen( GAMEINFOPATH_TOKEN ); - FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence ); + pszBaseDir = initInfo.m_pDirectoryName; } else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation ) { @@ -650,26 +589,117 @@ FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places. // In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots, // so we can just use the path as-is. - - // In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder). - pLocation += strlen( BASESOURCEPATHS_TOKEN ); + } - // Add the Orange-box path (which also will include whatever the depots mapped in as well if we're - // running a Steam-launched app). - FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); + CUtlStringList vecFullLocationPaths; + char szAbsSearchPath[MAX_PATH]; + V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), pLocation, pszBaseDir ); - if ( FileSystem_IsHldsUpdateToolDedicatedServer() ) - { - // If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too. - char ep1EraPath[MAX_PATH]; - V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation ); - FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence ); - } + // Now resolve any ./'s. + V_FixSlashes( szAbsSearchPath ); + if ( !V_RemoveDotSlashes( szAbsSearchPath ) ) + Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", szAbsSearchPath ); + V_StripTrailingSlash( szAbsSearchPath ); + + // Don't bother doing any wildcard expansion unless it has wildcards. This avoids the weird + // thing with xxx_dir.vpk files being referred to simply as xxx.vpk. + if ( V_stristr( pLocation, "?") == NULL && V_stristr( pLocation, "*") == NULL ) + { + vecFullLocationPaths.CopyAndAddToTail( szAbsSearchPath ); } else { - FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); + FileFindHandle_t findHandle = 0; + const char *pszFoundShortName = initInfo.m_pFileSystem->FindFirst( szAbsSearchPath, &findHandle ); + if ( pszFoundShortName ) + { + do + { + + // We only know how to mount VPK's and directories + if ( pszFoundShortName[0] != '.' && ( initInfo.m_pFileSystem->FindIsDirectory( findHandle ) || V_stristr( pszFoundShortName, ".vpk" ) ) ) + { + char szAbsName[MAX_PATH]; + V_ExtractFilePath( szAbsSearchPath, szAbsName, sizeof( szAbsName ) ); + V_AppendSlash( szAbsName, sizeof(szAbsName) ); + V_strcat_safe( szAbsName, pszFoundShortName ); + + vecFullLocationPaths.CopyAndAddToTail( szAbsName ); + + // Check for a common mistake + if ( + !V_stricmp( pszFoundShortName, "materials" ) + || !V_stricmp( pszFoundShortName, "maps" ) + || !V_stricmp( pszFoundShortName, "resource" ) + || !V_stricmp( pszFoundShortName, "scripts" ) + || !V_stricmp( pszFoundShortName, "sound" ) + || !V_stricmp( pszFoundShortName, "models" ) ) + { + + char szReadme[MAX_PATH]; + V_ExtractFilePath( szAbsSearchPath, szReadme, sizeof( szReadme ) ); + V_AppendSlash( szReadme, sizeof(szReadme) ); + V_strcat_safe( szReadme, "readme.txt" ); + + Error( + "Tried to add %s as a search path.\n" + "\nThis is probably not what you intended.\n" + "\nCheck %s for more info\n", + szAbsName, szReadme ); + } + + } + pszFoundShortName = initInfo.m_pFileSystem->FindNext( findHandle ); + } while ( pszFoundShortName ); + initInfo.m_pFileSystem->FindClose( findHandle ); + } + + // Sort alphabetically. Also note that this will put + // all the xxx_000.vpk packs just before the corresponding + // xxx_dir.vpk + vecFullLocationPaths.Sort( SortStricmp ); + + // Now for any _dir.vpk files, remove the _nnn.vpk ones. + int idx = vecFullLocationPaths.Count()-1; + while ( idx > 0 ) + { + char szTemp[ MAX_PATH ]; + V_strcpy_safe( szTemp, vecFullLocationPaths[ idx ] ); + --idx; + + char *szDirVpk = V_stristr( szTemp, "_dir.vpk" ); + if ( szDirVpk != NULL ) + { + *szDirVpk = '\0'; + while ( idx >= 0 ) + { + char *pszPath = vecFullLocationPaths[ idx ]; + if ( V_stristr( pszPath, szTemp ) != pszPath ) + break; + delete pszPath; + vecFullLocationPaths.Remove( idx ); + --idx; + } + } + } + } + + // Parse Path ID list + CUtlStringList vecPathIDs; + V_SplitString( pCur->GetName(), "+", vecPathIDs ); + FOR_EACH_VEC( vecPathIDs, idxPathID ) + { + Q_StripPrecedingAndTrailingWhitespace( vecPathIDs[ idxPathID ] ); + } + + // Mount them. + FOR_EACH_VEC( vecFullLocationPaths, idxLocation ) + { + FOR_EACH_VEC( vecPathIDs, idxPathID ) + { + FileSystem_AddLoadedSearchPath( initInfo, vecPathIDs[ idxPathID ], vecFullLocationPaths[ idxLocation ], bLowViolence ); + } } } @@ -679,15 +709,13 @@ FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) // when people forget to specify a search path. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "download", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true ); - if ( initInfo.m_ModPath[0] != 0 ) - { - // Add the write path last. - initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL ); - } + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "game_write", true ); + initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod_write", true ); #ifdef _DEBUG - initInfo.m_pFileSystem->PrintSearchPaths(); + // initInfo.m_pFileSystem->PrintSearchPaths(); #endif return FS_OK; @@ -954,60 +982,6 @@ bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath ) } } -FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings ) -{ - if ( IsConsole() ) - { - // consoles don't use steam - return FS_MISSING_STEAM_DLL; - } - - // Start at our bin directory and move up until we find a directory with steam.dll in it. - char executablePath[MAX_PATH]; - if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) - { - if ( bErrorsAsWarnings ) - { - Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" ); - return FS_INVALID_PARAMETERS; - } - else - { - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - } - } - - Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen ); - while ( 1 ) - { - // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username. - // find - if ( DoesFileExistIn( steamInstallPath, "steam.dll" ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) ) - break; - - if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) ) - { - if ( bErrorsAsWarnings ) - { - Warning( "Can't find steam.dll relative to executable path: %s.\n", executablePath ); - return FS_MISSING_STEAM_DLL; - } - else - { - return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find steam.dll relative to executable path: %s.", executablePath ); - } - } - } - - // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll. - char szPath[ 8192 ]; - steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) ); - if ( !DoesPathExistAlready( szPath, steamInstallPath ) ) - { - steamEnvVars.m_Path.SetValue( "%s;%s", szPath, steamInstallPath ); - } - return FS_OK; -} FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ) { @@ -1087,53 +1061,6 @@ void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars } } -void SetSteamAppId( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) -{ - // SteamAppId is in gameinfo.txt->FileSystem->FileSystemInfo_Steam->SteamAppId. - int iAppId = pFileSystemInfo->GetInt( "SteamAppId", -1 ); - if ( iAppId == -1 ) - Error( "Missing SteamAppId in %s\\%s.", pGameInfoDirectory, GAMEINFO_FILENAME ); - - steamEnvVars.m_SteamAppId.SetValue( "%d", iAppId ); -} - -FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars ) -{ - // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can. - char steamInfoFile[MAX_PATH]; - Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) ); - Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) ); - Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS ); - KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile ); - - char steamInstallPath[MAX_PATH]; - FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false ); - if ( ret != FS_OK ) - return ret; - - SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars ); - SetSteamUserPassphrase( pSteamInfo, steamEnvVars ); - SetSteamAppId( pFileSystemInfo, pGameInfoDirectory, steamEnvVars ); - - if ( pSteamInfo ) - pSteamInfo->deleteThis(); - - return FS_OK; -} - -FSReturnCode_t GetSteamExtraAppId( const char *pDirectoryName, int *nExtraAppId ) -{ - // Now, load gameinfo.txt (to make sure it's there) - KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; - FSReturnCode_t ret = LoadGameInfoFile( pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); - if ( ret != FS_OK ) - return ret; - - *nExtraAppId = pFileSystemInfo->GetInt( "ToolsAppId", -1 ); - pMainFile->deleteThis(); - return FS_OK; -} - FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ) { pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" ); @@ -1143,6 +1070,12 @@ FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem ) return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" ); + + if ( !FileSystem_GetBaseDir( executablePath, sizeof( executablePath ) ) ) + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); + + pFileSystem->AddSearchPath( executablePath, "BASE_PATH" ); + return FS_OK; } @@ -1158,41 +1091,31 @@ FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLe char executablePath[MAX_PATH]; if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) ) return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." ); - -#if defined( _WIN32 ) && !defined( _X360 ) - // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. - // There are two command line parameters for Steam: - // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) - // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR ); - if ( CommandLine()->FindParm( "-steam" ) || CommandLine()->FindParm( "-steamlocal" ) || _access( pFileSystemDLL, 0 ) != 0 ) - { - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam.dll", executablePath, CORRECT_PATH_SEPARATOR ); - bSteam = true; - } -#elif defined( _X360 ) - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio.dll", executablePath, CORRECT_PATH_SEPARATOR ); -#elif defined( _LINUX ) - Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_i486.so", executablePath, CORRECT_PATH_SEPARATOR ); -#else - #error "define a filesystem dll name" -#endif + + // Assume we'll use local files + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR ); + + #if !defined( _X360 ) + + // Use filsystem_steam if it exists? + #if defined( OSX ) || defined( LINUX ) + struct stat statBuf; + #endif + if ( + #if defined( OSX ) || defined( LINUX ) + stat( pFileSystemDLL, &statBuf ) != 0 + #else + _access( pFileSystemDLL, 0 ) != 0 + #endif + ) { + Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR ); + bSteam = true; + } + #endif return FS_OK; } -//----------------------------------------------------------------------------- -// Sets up the steam.dll install path in our PATH env var (so you can then just -// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special ) -//----------------------------------------------------------------------------- -FSReturnCode_t FileSystem_SetupSteamInstallPath() -{ - CSteamEnvVars steamEnvVars; - char steamInstallPath[MAX_PATH]; - FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); - steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - return ret; -} //----------------------------------------------------------------------------- // Sets up the steam environment + gets back the gameinfo.txt path @@ -1205,45 +1128,13 @@ FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo ) return ret; // This is so that processes spawned by this application will have the same VPROJECT +#ifdef WIN32 char pEnvBuf[MAX_PATH+32]; Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath ); _putenv( pEnvBuf ); - - CSteamEnvVars steamEnvVars; - if ( fsInfo.m_bSteam ) - { - if ( fsInfo.m_bToolsMode ) - { - // Now, load gameinfo.txt (to make sure it's there) - KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; - ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths ); - if ( ret != FS_OK ) - return ret; - - // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll. - // There are two command line parameters for Steam: - // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend) - // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD) - - // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam. - ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars ); - if ( ret != FS_OK ) - return ret; - - steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - - // We're done with main file - pMainFile->deleteThis(); - } - else if ( fsInfo.m_bSetSteamDLLPath ) - { - // This is used by the engine to automatically set the path to their steam.dll when running the engine, - // so they can debug it without having to copy steam.dll up into their hl2.exe folder. - char steamInstallPath[MAX_PATH]; - ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true ); - steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward. - } - } +#else + setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 ); +#endif return FS_OK; } @@ -1287,33 +1178,33 @@ FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo ) // This part is Steam-only. if ( mountContentInfo.m_pFileSystem->IsSteam() ) { - // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem - // like HL2, then mount the SDK content (tools materials and models, etc) in addition. - int nExtraAppId = -1; - if ( mountContentInfo.m_bToolsMode ) - { - FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId ); - if ( ret != FS_OK ) - return ret; - } + return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "Should not be using filesystem_steam anymore!" ); - // Set our working directory temporarily so Steam can remember it. - // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk - // to get to the relative part of the path. - char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH]; - if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) - return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); - - Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) ); - _chdir( baseDir ); - - // Filesystem_tools needs to add dependencies in here beforehand. - FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId ); - - _chdir( oldWorkingDir ); - - if ( retVal != FILESYSTEM_MOUNT_OK ) - return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" ); +// // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem +// // like HL2, then mount the SDK content (tools materials and models, etc) in addition. +// int nExtraAppId = -1; +// if ( mountContentInfo.m_bToolsMode ) +// { +// // !FIXME! Here we need to mount the tools content (VPK's) in some way...? +// } +// +// // Set our working directory temporarily so Steam can remember it. +// // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk +// // to get to the relative part of the path. +// char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH]; +// if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) +// return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); +// +// Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) ); +// _chdir( baseDir ); +// +// // Filesystem_tools needs to add dependencies in here beforehand. +// FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId ); +// +// _chdir( oldWorkingDir ); +// +// if ( retVal != FILESYSTEM_MOUNT_OK ) +// return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" ); } return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem ); @@ -1342,17 +1233,9 @@ void FileSystem_ClearSteamEnvVars() void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath ) { char platform[MAX_PATH]; - if ( pFileSystem->IsSteam() ) - { - // Steam doesn't support relative paths - Q_strncpy( platform, "platform", MAX_PATH ); - } - else - { - Q_strncpy( platform, szGameInfoPath, MAX_PATH ); - Q_StripTrailingSlash( platform ); - Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH ); - } + Q_strncpy( platform, szGameInfoPath, MAX_PATH ); + Q_StripTrailingSlash( platform ); + Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH ); pFileSystem->AddSearchPath( platform, "PLATFORM" ); } diff --git a/public/filesystem_init.h b/public/filesystem_init.h index fc48b03f..e9787ab7 100644 --- a/public/filesystem_init.h +++ b/public/filesystem_init.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -148,14 +148,17 @@ public: // This specifies the directory where gameinfo.txt is. This must be set. const char *m_pDirectoryName; - // If this is set, then it will add a search path with _ appended to the pathname - // for each search path with a path ID of "game". + // If this is set, then any search paths with a _english will be replaced with _m_pLanguage and added before the + // _english path // (default: null) const char *m_pLanguage; // This is the filesystem FileSystem_LoadSearchPaths is talking to. IFileSystem *m_pFileSystem; + bool m_bMountHDContent; + bool m_bLowViolence; + // Outputs. public: // This is the location of the first search path called "game", which also becomes your "mod" search path. @@ -206,11 +209,6 @@ void FileSystem_ClearSteamEnvVars(); // Find the steam.cfg above you for optional stuff FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen ); -// Setup the Steam.dll path without needing all the extra gameinfo stuff first -// used by the CSteamApplication::Create() code to LoadModule() on the filesystem -// before the underlying apps know specific details about the environment to load -FSReturnCode_t FileSystem_SetupSteamInstallPath(); - // Returns the last error. const char *FileSystem_GetLastErrorString(); diff --git a/public/tier0/basetypes.h b/public/tier0/basetypes.h index d488c12f..96d4c73b 100644 --- a/public/tier0/basetypes.h +++ b/public/tier0/basetypes.h @@ -19,6 +19,12 @@ #endif +// This is a trick to get the DLL extension off the -D option on the command line. +#define DLLExtTokenPaste(x) #x +#define DLLExtTokenPaste2(x) DLLExtTokenPaste(x) +#define DLL_EXT_STRING DLLExtTokenPaste2( _DLL_EXT ) + + #include "protected_things.h" // There's a different version of this file in the xbox codeline diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 859ba16e..3d63cdbd 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -337,6 +337,9 @@ typedef void * HINSTANCE; #define ALIGN128 DECL_ALIGN(128) +// Pull in the /analyze code annotations. +#include "annotations.h" + // Linux had a few areas where it didn't construct objects in the same order that Windows does. // So when CVProfile::CVProfile() would access g_pMemAlloc, it would crash because the allocator wasn't initalized yet. #if defined(_LINUX) || defined(__APPLE__) diff --git a/public/tier1/interface.h b/public/tier1/interface.h index 00beb450..c7e1bdce 100644 --- a/public/tier1/interface.h +++ b/public/tier1/interface.h @@ -179,12 +179,18 @@ extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); extern CreateInterfaceFn Sys_GetFactoryThis( void ); +enum Sys_Flags +{ + SYS_NOFLAGS = 0x00, + SYS_NOLOAD = 0x01 // no loading, no ref-counting, only returns handle if lib is loaded. +}; + //----------------------------------------------------------------------------- // Load & Unload should be called in exactly one place for each module // The factory for that module should be passed on to dependent components for // proper versioning. //----------------------------------------------------------------------------- -extern CSysModule *Sys_LoadModule( const char *pModuleName ); +extern CSysModule *Sys_LoadModule( const char *pModuleName, Sys_Flags flags = SYS_NOFLAGS ); extern void Sys_UnloadModule( CSysModule *pModule ); // This is a helper function to load a module, get its factory, and get a specific interface. diff --git a/public/tier1/smartptr.h b/public/tier1/smartptr.h index fa17a792..a0065e5a 100644 --- a/public/tier1/smartptr.h +++ b/public/tier1/smartptr.h @@ -1,4 +1,4 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// +//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -107,14 +107,14 @@ template < typename T > class CArrayAutoPtr : public CPlainAutoPtr < T > // Warning: no polymorphic destructor (delete on base class will be a mistake) { public: - explicit CArrayAutoPtr( T *p = NULL ) { Attach( p ); } - ~CArrayAutoPtr( void ) { Delete(); } + explicit CArrayAutoPtr( T *p = NULL ) { this->Attach( p ); } + ~CArrayAutoPtr( void ) { this->Delete(); } public: - void Delete( void ) { delete [] Detach(); } + void Delete( void ) { delete [] CPlainAutoPtr < T >::Detach(); } public: - T & operator [] ( int k ) const { return Get()[ k ]; } + T & operator [] ( int k ) const { return CPlainAutoPtr < T >::Get()[ k ]; } }; diff --git a/public/tier1/strtools.h b/public/tier1/strtools.h index b699c85e..c6b3ec07 100644 --- a/public/tier1/strtools.h +++ b/public/tier1/strtools.h @@ -17,6 +17,7 @@ #include #include #include +#include #endif #include @@ -137,13 +138,24 @@ void V_normalizeFloatString( char* pFloat ); // pDest[maxLen-1] is always NULL terminated if pSrc's length is >= maxLen. // // This means the last parameter can usually be a sizeof() of a string. -void V_strncpy( char *pDest, const char *pSrc, int maxLen ); +void V_strncpy( OUT_Z_CAP(maxLenInChars) char *pDest, const char *pSrc, int maxLenInChars ); + +// Ultimate safe strcpy function, for arrays only -- buffer size is inferred by the compiler +template void V_strcpy_safe( OUT_Z_ARRAY char (&pDest)[maxLenInChars], const char *pSrc ) +{ + V_strncpy( pDest, pSrc, (int)maxLenInChars ); +} + int V_snprintf( char *pDest, int destLen, const char *pFormat, ... ); void V_wcsncpy( wchar_t *pDest, wchar_t const *pSrc, int maxLenInBytes ); int V_snwprintf( wchar_t *pDest, int destLen, const wchar_t *pFormat, ... ); #define COPY_ALL_CHARACTERS -1 -char *V_strncat(char *, const char *, size_t destBufferSize, int max_chars_to_copy=COPY_ALL_CHARACTERS ); +char *V_strncat( INOUT_Z_CAP(cchDest) char *pDest, const char *pSrc, size_t cchDest, int max_chars_to_copy=COPY_ALL_CHARACTERS ); +template char *V_strcat_safe( INOUT_Z_ARRAY char (&pDest)[cchDest], const char *pSrc, int nMaxCharsToCopy=COPY_ALL_CHARACTERS ) +{ + return V_strncat( pDest, pSrc, (int)cchDest, nMaxCharsToCopy ); +} char *V_strnlwr(char *, size_t); @@ -169,16 +181,20 @@ typedef char * va_list; #endif // _VA_LIST_DEFINED -#elif defined(_LINUX) || defined(__APPLE__) +#elif POSIX #include #endif #ifdef _WIN32 #define CORRECT_PATH_SEPARATOR '\\' +#define CORRECT_PATH_SEPARATOR_S "\\" #define INCORRECT_PATH_SEPARATOR '/' -#elif defined(_LINUX) || defined(__APPLE__) +#define INCORRECT_PATH_SEPARATOR_S "/" +#elif POSIX #define CORRECT_PATH_SEPARATOR '/' +#define CORRECT_PATH_SEPARATOR_S "/" #define INCORRECT_PATH_SEPARATOR '\\' +#define INCORRECT_PATH_SEPARATOR_S "\\" #endif int V_vsnprintf( char *pDest, int maxLen, const char *pFormat, va_list params ); @@ -193,6 +209,16 @@ char *V_pretifynum( int64 value ); int V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes ); int V_UnicodeToUTF8( const wchar_t *pUnicode, char *pUTF8, int cubDestSizeInBytes ); + +// strips leading and trailing whitespace; returns true if any characters were removed. UTF-8 and UTF-16 versions. +bool Q_StripPrecedingAndTrailingWhitespace( char *pch ); +bool Q_StripPrecedingAndTrailingWhitespaceW( wchar_t *pwch ); + +// strips leading and trailing whitespace, also taking "aggressive" characters +// like punctuation spaces, non-breaking spaces, composing characters, and so on +bool Q_AggressiveStripPrecedingAndTrailingWhitespace( char *pch ); +bool Q_AggressiveStripPrecedingAndTrailingWhitespaceW( wchar_t *pwch ); +bool Q_RemoveAllEvilCharacters( char *pch ); // Functions for converting hexidecimal character strings back into binary data etc. // // e.g., diff --git a/public/tier1/utlmemory.h b/public/tier1/utlmemory.h index afb027dd..232efcfc 100644 --- a/public/tier1/utlmemory.h +++ b/public/tier1/utlmemory.h @@ -17,6 +17,7 @@ #include "tier0/dbg.h" #include #include "tier0/platform.h" +#include "mathlib/mathlib.h" #include "tier0/memalloc.h" #include "tier0/memdbgon.h" diff --git a/public/tier1/utlvector.h b/public/tier1/utlvector.h index 3dd9186e..9b04d00d 100644 --- a/public/tier1/utlvector.h +++ b/public/tier1/utlvector.h @@ -790,5 +790,68 @@ void CUtlVector::Validate( CValidator &validator, char *pchName ) } #endif // DBGFLAG_VALIDATE +// A vector class for storing pointers, so that the elements pointed to by the pointers are deleted +// on exit. +template class CUtlVectorAutoPurge : public CUtlVector< T, CUtlMemory< T, int> > +{ +public: + ~CUtlVectorAutoPurge( void ) + { + this->PurgeAndDeleteElements(); + } + +}; + +// easy string list class with dynamically allocated strings. For use with V_SplitString, etc. +// Frees the dynamic strings in destructor. +class CUtlStringList : public CUtlVectorAutoPurge< char *> +{ +public: + void CopyAndAddToTail( char const *pString ) // clone the string and add to the end + { + char *pNewStr = new char[1 + strlen( pString )]; + V_strcpy( pNewStr, pString ); + AddToTail( pNewStr ); + } + + static int __cdecl SortFunc( char * const * sz1, char * const * sz2 ) + { + return strcmp( *sz1, *sz2 ); + } + + inline void PurgeAndDeleteElements() + { + for( int i=0; i < m_Size; i++ ) + { + delete [] Element(i); + } + Purge(); + } + + ~CUtlStringList( void ) + { + this->PurgeAndDeleteElements(); + } +}; + + + +// placing it here a few days before Cert to minimize disruption to the rest of codebase +class CSplitString: public CUtlVector > +{ +public: + CSplitString(const char *pString, const char *pSeparator); + CSplitString(const char *pString, const char **pSeparators, int nSeparators); + ~CSplitString(); + // + // NOTE: If you want to make Construct() public and implement Purge() here, you'll have to free m_szBuffer there + // +private: + void Construct(const char *pString, const char **pSeparators, int nSeparators); + void PurgeAndDeleteElements(); +private: + char *m_szBuffer; // a copy of original string, with '\0' instead of separators +}; + #endif // CCVECTOR_H diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp index 897d53c0..6d2cf110 100644 --- a/tier1/KeyValues.cpp +++ b/tier1/KeyValues.cpp @@ -229,12 +229,12 @@ class CKeyValuesGrowableStringTable public: // Constructor CKeyValuesGrowableStringTable() : + m_hashLookup( 2048, 0, 0, m_Functor, m_Functor ), #ifdef PLATFORM_64BITS m_vecStrings( 0, 4 * 512 * 1024 ) #else m_vecStrings( 0, 512 * 1024 ) #endif - , m_hashLookup( 2048, 0, 0, m_Functor, m_Functor ) { m_vecStrings.AddToTail( '\0' ); } @@ -1465,14 +1465,14 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul bool KeyValues::GetBool( const char *keyName, bool defaultValue, bool* optGotDefault ) { if ( FindKey( keyName ) ) - { - if ( optGotDefault ) - (*optGotDefault) = false; + { + if ( optGotDefault ) + (*optGotDefault) = false; return 0 != GetInt( keyName, 0 ); - } + } - if ( optGotDefault ) - (*optGotDefault) = true; + if ( optGotDefault ) + (*optGotDefault) = true; return defaultValue; } @@ -3054,4 +3054,4 @@ bool CKeyValuesDumpContextAsDevMsg::KvWriteText( char const *szText ) Msg( "%s", szText ); } return true; -} \ No newline at end of file +} diff --git a/tier1/generichash.cpp b/tier1/generichash.cpp index af784fc4..48f62c51 100644 --- a/tier1/generichash.cpp +++ b/tier1/generichash.cpp @@ -137,12 +137,12 @@ unsigned FASTCALL HashStringCaselessConventional( const char *pszKey ) //----------------------------------------------------------------------------- unsigned FASTCALL HashInt( const int n ) { - register unsigned even, odd; + unsigned even, odd; even = g_nRandomValues[n & 0xff]; odd = g_nRandomValues[((n >> 8) & 0xff)]; even = g_nRandomValues[odd ^ (n >> 24)]; - odd = g_nRandomValues[even ^ (n >> 16) & 0xff]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; odd = g_nRandomValues[even ^ (n & 0xff)]; @@ -154,16 +154,16 @@ unsigned FASTCALL HashInt( const int n ) //----------------------------------------------------------------------------- unsigned FASTCALL Hash4( const void *pKey ) { - register const uint32 * p = (const uint32 *) pKey; - register unsigned even, - odd, - n; + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; n = *p; even = g_nRandomValues[n & 0xff]; odd = g_nRandomValues[((n >> 8) & 0xff)]; even = g_nRandomValues[odd ^ (n >> 24)]; - odd = g_nRandomValues[even ^ (n >> 16) & 0xff]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; odd = g_nRandomValues[even ^ (n & 0xff)]; @@ -176,16 +176,16 @@ unsigned FASTCALL Hash4( const void *pKey ) //----------------------------------------------------------------------------- unsigned FASTCALL Hash8( const void *pKey ) { - register const uint32 * p = (const uint32 *) pKey; - register unsigned even, - odd, - n; + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; n = *p; even = g_nRandomValues[n & 0xff]; odd = g_nRandomValues[((n >> 8) & 0xff)]; even = g_nRandomValues[odd ^ (n >> 24)]; - odd = g_nRandomValues[even ^ (n >> 16) & 0xff]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; odd = g_nRandomValues[even ^ (n & 0xff)]; @@ -204,16 +204,16 @@ unsigned FASTCALL Hash8( const void *pKey ) //----------------------------------------------------------------------------- unsigned FASTCALL Hash12( const void *pKey ) { - register const uint32 * p = (const uint32 *) pKey; - register unsigned even, - odd, - n; + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; n = *p; even = g_nRandomValues[n & 0xff]; odd = g_nRandomValues[((n >> 8) & 0xff)]; even = g_nRandomValues[odd ^ (n >> 24)]; - odd = g_nRandomValues[even ^ (n >> 16) & 0xff]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; odd = g_nRandomValues[even ^ (n & 0xff)]; @@ -238,16 +238,16 @@ unsigned FASTCALL Hash12( const void *pKey ) //----------------------------------------------------------------------------- unsigned FASTCALL Hash16( const void *pKey ) { - register const uint32 * p = (const uint32 *) pKey; - register unsigned even, - odd, - n; + const uint32 * p = (const uint32 *) pKey; + unsigned even, + odd, + n; n = *p; even = g_nRandomValues[n & 0xff]; odd = g_nRandomValues[((n >> 8) & 0xff)]; even = g_nRandomValues[odd ^ (n >> 24)]; - odd = g_nRandomValues[even ^ (n >> 16) & 0xff]; + odd = g_nRandomValues[even ^ ((n >> 16) & 0xff)]; even = g_nRandomValues[odd ^ ((n >> 8) & 0xff)]; odd = g_nRandomValues[even ^ (n & 0xff)]; diff --git a/tier1/interface.cpp b/tier1/interface.cpp index 976465b0..d07dcc9f 100644 --- a/tier1/interface.cpp +++ b/tier1/interface.cpp @@ -142,53 +142,59 @@ struct ThreadedLoadLibaryContext_t #ifdef _WIN32 // wraps LoadLibraryEx() since 360 doesn't support that -static HMODULE InternalLoadLibrary( const char *pName ) +static HMODULE InternalLoadLibrary( const char *pName, Sys_Flags flags ) { #if defined(_X360) return LoadLibrary( pName ); #else - return LoadLibraryEx( pName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); + if ( flags & SYS_NOLOAD ) + return GetModuleHandle( pName ); + else + return LoadLibraryEx( pName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); #endif } unsigned ThreadedLoadLibraryFunc( void *pParam ) { ThreadedLoadLibaryContext_t *pContext = (ThreadedLoadLibaryContext_t*)pParam; - pContext->m_hLibrary = InternalLoadLibrary(pContext->m_pLibraryName); + pContext->m_hLibrary = InternalLoadLibrary( pContext->m_pLibraryName, SYS_NOFLAGS ); return 0; } -#endif -HMODULE Sys_LoadLibrary( const char *pLibraryName ) +#endif // _WIN32 + +HMODULE Sys_LoadLibrary( const char *pLibraryName, Sys_Flags flags ) { - char str[1024]; -#if defined( _WIN32 ) && !defined( _X360 ) - const char *pModuleExtension = ".dll"; - const char *pModuleAddition = pModuleExtension; -#elif defined( _X360 ) - const char *pModuleExtension = "_360.dll"; - const char *pModuleAddition = pModuleExtension; -#elif defined( _LINUX ) - const char *pModuleExtension = ".so"; - const char *pModuleAddition = "_i486.so"; // if an extension is on the filename assume the i486 binary set -#elif defined( __APPLE__ ) - const char *pModuleExtension = ".dylib"; - const char *pModuleAddition = ".dylib"; -#endif + char str[ 1024 ]; + // Note: DLL_EXT_STRING can be "_srv.so" or "_360.dll". So be careful + // when using the V_*Extension* routines... + const char *pDllStringExtension = V_GetFileExtension( DLL_EXT_STRING ); + const char *pModuleExtension = pDllStringExtension ? ( pDllStringExtension - 1 ) : DLL_EXT_STRING; + Q_strncpy( str, pLibraryName, sizeof(str) ); - if ( !Q_stristr( str, pModuleExtension ) ) + + if ( IsX360() ) { - if ( IsX360() ) + // old, probably busted, behavior for xbox + if ( !Q_stristr( str, pModuleExtension ) ) { - Q_StripExtension( str, str, sizeof(str) ); + V_SetExtension( str, pModuleExtension, sizeof(str) ); } - Q_strncat( str, pModuleAddition, sizeof(str) ); } + else + { + // always force the final extension to be .dll + V_SetExtension( str, pModuleExtension, sizeof(str) ); + } + Q_FixSlashes( str ); #ifdef _WIN32 ThreadedLoadLibraryFunc_t threadFunc = GetThreadedLoadLibraryFunc(); if ( !threadFunc ) - return InternalLoadLibrary( str ); + return InternalLoadLibrary( str, flags ); + + // We shouldn't be passing noload while threaded. + Assert( !( flags & SYS_NOLOAD ) ); ThreadedLoadLibaryContext_t context; context.m_pLibraryName = str; @@ -209,28 +215,33 @@ HMODULE Sys_LoadLibrary( const char *pLibraryName ) ReleaseThreadHandle( h ); return context.m_hLibrary; -#elif defined _LINUX || defined __APPLE__ - HMODULE ret = dlopen( str, RTLD_NOW ); - if ( ! ret ) +#elif POSIX + int dlopen_mode = RTLD_NOW; + + if ( flags & SYS_NOLOAD ) + dlopen_mode |= RTLD_NOLOAD; + + HMODULE ret = ( HMODULE )dlopen( str, dlopen_mode ); + if ( !ret && !( flags & SYS_NOLOAD ) ) { const char *pError = dlerror(); - if ( pError && ( strstr( pError, "No such file" ) == 0 ) ) + if ( pError && ( strstr( pError, "No such file" ) == 0 ) && ( strstr( pError, "image not found" ) == 0 ) ) { Msg( " failed to dlopen %s error=%s\n", str, pError ); - } } return ret; #endif } +static bool s_bRunningWithDebugModules = false; //----------------------------------------------------------------------------- // Purpose: Loads a DLL/component from disk and returns a handle to it // Input : *pModuleName - filename of the component // Output : opaque handle to the module (hides system dependency) //----------------------------------------------------------------------------- -CSysModule *Sys_LoadModule( const char *pModuleName ) +CSysModule *Sys_LoadModule( const char *pModuleName, Sys_Flags flags /* = SYS_NOFLAGS (0) */ ) { // If using the Steam filesystem, either the DLL must be a minimum footprint // file in the depot (MFP) or a filesystem GetLocalCopy() call must be made @@ -247,7 +258,7 @@ CSysModule *Sys_LoadModule( const char *pModuleName ) int i = CommandLine()->FindParm( "-basedir" ); if ( i ) { - strcpy( szCwd, CommandLine()->GetParm( i+1 ) ); + V_strcpy_safe( szCwd, CommandLine()->GetParm( i + 1 ) ); } } if (szCwd[strlen(szCwd) - 1] == '/' || szCwd[strlen(szCwd) - 1] == '\\' ) @@ -256,7 +267,8 @@ CSysModule *Sys_LoadModule( const char *pModuleName ) } char szAbsoluteModuleName[1024]; - if ( strstr( pModuleName, "bin/") == pModuleName ) + size_t cCwd = strlen( szCwd ); + if ( strstr( pModuleName, "bin/") == pModuleName || ( szCwd[ cCwd - 1 ] == 'n' && szCwd[ cCwd - 2 ] == 'i' && szCwd[ cCwd - 3 ] == 'b' ) ) { // don't make bin/bin path Q_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", szCwd, pModuleName ); @@ -265,13 +277,13 @@ CSysModule *Sys_LoadModule( const char *pModuleName ) { Q_snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/bin/%s", szCwd, pModuleName ); } - hDLL = Sys_LoadLibrary( szAbsoluteModuleName ); + hDLL = Sys_LoadLibrary( szAbsoluteModuleName, flags ); } if ( !hDLL ) { // full path failed, let LoadLibrary() try to search the PATH now - hDLL = Sys_LoadLibrary( pModuleName ); + hDLL = Sys_LoadLibrary( pModuleName, flags ); #if defined( _DEBUG ) if ( !hDLL ) { @@ -293,28 +305,69 @@ CSysModule *Sys_LoadModule( const char *pModuleName ) LocalFree( (HLOCAL)lpMsgBuf ); #elif defined( _X360 ) - Msg( "Failed to load %s:\n", pModuleName ); + DWORD error = GetLastError(); + Msg( "Error(%d) - Failed to load %s:\n", error, pModuleName ); #else - Error( "Failed to load %s: %s\n", pModuleName, dlerror() ); + Msg( "Failed to load %s: %s\n", pModuleName, dlerror() ); #endif // _WIN32 } #endif // DEBUG } +#if !defined(LINUX) // If running in the debugger, assume debug binaries are okay, otherwise they must run with -allowdebug - if ( !IsX360() && hDLL && - !CommandLine()->FindParm( "-allowdebug" ) && - !Sys_IsDebuggerPresent() ) + if ( Sys_GetProcAddress( hDLL, "BuiltDebug" ) ) { - if ( Sys_GetProcAddress( hDLL, "BuiltDebug" ) ) + if ( !IsX360() && hDLL && + !CommandLine()->FindParm( "-allowdebug" ) && + !Sys_IsDebuggerPresent() ) { Error( "Module %s is a debug build\n", pModuleName ); } + + DevWarning( "Module %s is a debug build\n", pModuleName ); + + if ( !s_bRunningWithDebugModules ) + { + s_bRunningWithDebugModules = true; + +#if 0 //def IS_WINDOWS_PC + char chMemoryName[ MAX_PATH ]; + DebugKernelMemoryObjectName( chMemoryName ); + + (void) CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, chMemoryName ); + // Created a shared memory kernel object specific to process id + // Existence of this object indicates that we have debug modules loaded +#endif + } } +#endif return reinterpret_cast(hDLL); } +//----------------------------------------------------------------------------- +// Purpose: Determine if any debug modules were loaded +//----------------------------------------------------------------------------- +bool Sys_RunningWithDebugModules() +{ + if ( !s_bRunningWithDebugModules ) + { +#if 0 //def IS_WINDOWS_PC + char chMemoryName[ MAX_PATH ]; + DebugKernelMemoryObjectName( chMemoryName ); + + HANDLE hObject = OpenFileMapping( FILE_MAP_READ, FALSE, chMemoryName ); + if ( hObject && hObject != INVALID_HANDLE_VALUE ) + { + CloseHandle( hObject ); + s_bRunningWithDebugModules = true; + } +#endif + } + return s_bRunningWithDebugModules; +} + //----------------------------------------------------------------------------- // Purpose: Unloads a DLL/component from diff --git a/tier1/strtools.cpp b/tier1/strtools.cpp index e7943983..c397724a 100644 --- a/tier1/strtools.cpp +++ b/tier1/strtools.cpp @@ -992,9 +992,240 @@ char *V_pretifynum( int64 value ) return out; } +//----------------------------------------------------------------------------- +// Purpose: returns true if a wide character is a "mean" space; that is, +// if it is technically a space or punctuation, but causes disruptive +// behavior when used in names, web pages, chat windows, etc. +// +// characters in this set are removed from the beginning and/or end of strings +// by Q_AggressiveStripPrecedingAndTrailingWhitespaceW() +//----------------------------------------------------------------------------- +bool Q_IsMeanSpaceW( wchar_t wch ) +{ + bool bIsMean = false; + + switch ( wch ) + { + case L'\x0082': // BREAK PERMITTED HERE + case L'\x0083': // NO BREAK PERMITTED HERE + case L'\x00A0': // NO-BREAK SPACE + case L'\x034F': // COMBINING GRAPHEME JOINER + case L'\x2000': // EN QUAD + case L'\x2001': // EM QUAD + case L'\x2002': // EN SPACE + case L'\x2003': // EM SPACE + case L'\x2004': // THICK SPACE + case L'\x2005': // MID SPACE + case L'\x2006': // SIX SPACE + case L'\x2007': // figure space + case L'\x2008': // PUNCTUATION SPACE + case L'\x2009': // THIN SPACE + case L'\x200A': // HAIR SPACE + case L'\x200B': // ZERO-WIDTH SPACE + case L'\x200C': // ZERO-WIDTH NON-JOINER + case L'\x200D': // ZERO WIDTH JOINER + case L'\x200E': // LEFT-TO-RIGHT MARK + case L'\x2028': // LINE SEPARATOR + case L'\x2029': // PARAGRAPH SEPARATOR + case L'\x202F': // NARROW NO-BREAK SPACE + case L'\x2060': // word joiner + case L'\xFEFF': // ZERO-WIDTH NO BREAK SPACE + case L'\xFFFC': // OBJECT REPLACEMENT CHARACTER + bIsMean = true; + break; + } + + return bIsMean; +} + //----------------------------------------------------------------------------- -// Purpose: Converts a UTF8 string into a unicode string +// Purpose: strips trailing whitespace; returns pointer inside string just past +// any leading whitespace. +// +// bAggresive = true causes this function to also check for "mean" spaces, +// which we don't want in persona names or chat strings as they're disruptive +// to the user experience. +//----------------------------------------------------------------------------- +static wchar_t *StripWhitespaceWorker( int cchLength, wchar_t *pwch, bool *pbStrippedWhitespace, bool bAggressive ) +{ + // walk backwards from the end of the string, killing any whitespace + *pbStrippedWhitespace = false; + + wchar_t *pwchEnd = pwch + cchLength; + while ( --pwchEnd >= pwch ) + { + if ( !iswspace( *pwchEnd ) && ( !bAggressive || !Q_IsMeanSpaceW( *pwchEnd ) ) ) + break; + + *pwchEnd = 0; + *pbStrippedWhitespace = true; + } + + // walk forward in the string + while ( pwch < pwchEnd ) + { + if ( !iswspace( *pwch ) ) + break; + + *pbStrippedWhitespace = true; + pwch++; + } + + return pwch; +} + +//----------------------------------------------------------------------------- +// Purpose: Strips all evil characters (ie. zero-width no-break space) +// from a string. +//----------------------------------------------------------------------------- +bool Q_RemoveAllEvilCharacters( char *pch ) +{ + // convert to unicode + int cch = Q_strlen( pch ); + int cubDest = (cch + 1 ) * sizeof( wchar_t ); + wchar_t *pwch = (wchar_t *)stackalloc( cubDest ); + int cwch = Q_UTF8ToUnicode( pch, pwch, cubDest ) / sizeof( wchar_t ); + + bool bStrippedWhitespace = false; + + // Walk through and skip over evil characters + int nWalk = 0; + for( int i=0; i