diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/utils/vpccrccheck | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/utils/vpccrccheck')
| -rw-r--r-- | external/vpc/utils/vpccrccheck/crccheck_shared.cpp | 590 | ||||
| -rw-r--r-- | external/vpc/utils/vpccrccheck/crccheck_shared.h | 34 | ||||
| -rw-r--r-- | external/vpc/utils/vpccrccheck/vpccrccheck.cpp | 16 | ||||
| -rw-r--r-- | external/vpc/utils/vpccrccheck/vpccrccheck.vpc | 33 |
4 files changed, 673 insertions, 0 deletions
diff --git a/external/vpc/utils/vpccrccheck/crccheck_shared.cpp b/external/vpc/utils/vpccrccheck/crccheck_shared.cpp new file mode 100644 index 0000000..7ca8cb3 --- /dev/null +++ b/external/vpc/utils/vpccrccheck/crccheck_shared.cpp @@ -0,0 +1,590 @@ + +#include "../vpc/vpc.h" +#include "crccheck_shared.h" +#include "tier1/checksum_crc.h" +#include "tier1/strtools.h" +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#ifdef _WIN32 +#include <process.h> +#else +#include <stdlib.h> +#define stricmp strcasecmp +#endif + +#pragma warning( disable : 4996 ) +#pragma warning( disable : 4127 ) + +#define MAX_INCLUDE_STACK_DEPTH 10 + + +static bool IsValidPathChar( char token ) +{ + // does it look like a file? If this ends up too tight, can probably just check that it's not '[' or '{' + // cause conditional blocks are what we really want to avoid. + return isalpha(token) || isdigit(token) || (token == '.') || (token == '\\') || (token == '/'); +} + +extern const char *g_szArrPlatforms[]; +static void BuildReplacements( const char *token, char *szReplacements ) +{ + // Now go pickup the any files that exist, but were non-matches + *szReplacements = '\0'; + for ( int i = 0; g_szArrPlatforms[i] != NULL; i++ ) + { + char szPath[MAX_PATH]; + char szPathExpanded[MAX_PATH]; + + V_strncpy( szPath, token, sizeof(szPath) ); + Sys_ReplaceString( szPath, "$os", g_szArrPlatforms[i], szPathExpanded, sizeof(szPathExpanded) ); + V_FixSlashes( szPathExpanded ); + V_RemoveDotSlashes( szPathExpanded ); + V_FixDoubleSlashes( szPathExpanded ); + + // this fopen is probably using a relative path, but that's ok, as everything in + // the crc code is opening relative paths and assuming the cwd is set ok. + FILE *f = fopen( szPathExpanded, "rb" ); + if ( f ) + { + fclose(f); + // strcat - blech + strcat( szReplacements, g_szArrPlatforms[i] ); // really just need to stick the existing platforms seen in + strcat( szReplacements, ";" ); + } + + } +} + +static const char * GetToken( const char *ln, char *token ) +{ + *token = '\0'; + + while ( *ln && isspace(*ln) ) + ln++; + + if (!ln[0]) + return NULL; + + if ( ln[0] == '"' ) + { // does vpc allow \" inside the filename string - shouldn't matter, but we're going to assume no. + ln++; + while (*ln) + { + if ( ln[0] == '"' ) + break; + *token++ = *ln++; + } + *token = '\0'; + } + else if ( IsValidPathChar( *ln ) ) + { + while (*ln) + { + if ( isspace(*ln) ) + break; + *token++ = *ln++; + } + *token = '\0'; + } + else + { + token[0] = ln[0]; + token[1] = '\0'; + } + + return ln; +} + +static void PerformFileSubstitions( char * line, int linelen ) +{ + static bool bFindFilePending = false; + const char *ln = line; + + if ( !bFindFilePending ) + { + ln = V_stristr( ln, "$file " ); + if ( ln ) + bFindFilePending = true; + } + + if ( bFindFilePending ) + { + char token[1024]; + ln = GetToken( ln, token ); + if ( !ln ) + return; // no more tokens on line, we should try the next line + + bFindFilePending = false; + + + if ( V_stristr(token, "$os") ) + { + if ( !IsValidPathChar( *token ) ) + fprintf( stderr, "Warning: can't expand %s for crc calculation. Changes to this file set won't trigger automatic rebuild\n", token ); + char szReplacements[2048]; + char buffer[4096]; + BuildReplacements( token, szReplacements ); + Sys_ReplaceString( line, "$os", szReplacements, buffer, sizeof(buffer) ); + V_strncpy( line, buffer, linelen ); + } + } + + static bool bFindFilePatternPending = false; + ln = line; + + if ( !bFindFilePatternPending ) + { + ln = V_stristr( ln, "$filepattern" ); + while ( ln ) + { + ln += 13; + if ( isspace( ln[-1] ) ) + { + bFindFilePatternPending = true; + break; + } + } + } + + if ( bFindFilePatternPending ) + { + char token[1024]; + ln = GetToken( ln, token ); + if ( !ln ) + return; // no more tokens on line, we should try the next line + + bFindFilePatternPending = false; + + char szReplacements[2048]; szReplacements[0] = '\0'; + char buffer[4096]; + CUtlVector< CUtlString > vecResults; + Sys_ExpandFilePattern( token, vecResults ); + if ( vecResults.Count() ) + { + for ( int i= 0; i < vecResults.Count(); i++ ) + { + V_strncat( szReplacements, CFmtStr( "%s;", vecResults[i].String() ).Access(), V_ARRAYSIZE( szReplacements ) ); + } + + CRC32_t nCRC = CRC32_ProcessSingleBuffer( szReplacements, V_strlen( szReplacements ) ); + + Sys_ReplaceString( line, token, CFmtStr( "%s:%u", token, nCRC ).Access(), buffer, sizeof(buffer) ); + V_strncpy( line, buffer, linelen ); + } + else + { + if ( !IsValidPathChar( *token ) ) + fprintf( stderr, "Warning: %s couldn't be expanded during crc calculation. Changes to this file set won't trigger automatic project rebuild\n", token ); + } + } + +} + + +//----------------------------------------------------------------------------- +// Sys_Error +// +//----------------------------------------------------------------------------- +void Sys_Error( const char* format, ... ) +{ + va_list argptr; + + va_start( argptr,format ); + vfprintf( stderr, format, argptr ); + va_end( argptr ); + + exit( 1 ); +} + + +void SafeSnprintf( char *pOut, int nOutLen, const char *pFormat, ... ) +{ + va_list marker; + va_start( marker, pFormat ); + V_vsnprintf( pOut, nOutLen, pFormat, marker ); + va_end( marker ); + + pOut[nOutLen-1] = 0; +} + + +// for linked lists of strings +struct StringNode_t +{ + StringNode_t *m_pNext; + char m_Text[1]; // the string data +}; + + +static StringNode_t *MakeStrNode( char const *pStr ) +{ + size_t nLen = strlen( pStr ); + StringNode_t *nRet = ( StringNode_t * ) new unsigned char[sizeof( StringNode_t ) + nLen ]; + strcpy( nRet->m_Text, pStr ); + return nRet; +} + +//----------------------------------------------------------------------------- +// Sys_LoadTextFileWithIncludes +//----------------------------------------------------------------------------- +int Sys_LoadTextFileWithIncludes( const char* filename, char** bufferptr, bool bInsertFileMacroExpansion ) +{ + FILE *pFileStack[MAX_INCLUDE_STACK_DEPTH]; + int nSP = MAX_INCLUDE_STACK_DEPTH; + + StringNode_t *pFileLines = NULL; // tail ptr for fast adds + + size_t nTotalFileBytes = 0; + FILE *handle = fopen( filename, "r" ); + if ( !handle ) + return -1; + + pFileStack[--nSP] = handle; // push + while ( nSP < MAX_INCLUDE_STACK_DEPTH ) + { + // read lines + for (;;) + { + char lineBuffer[4096]; + char *ln = fgets( lineBuffer, sizeof( lineBuffer ), pFileStack[nSP] ); + if ( !ln ) + break; // out of text + + ln += strspn( ln, "\t " ); // skip white space + + // Need to insert actual files to make sure crc changes if disk-matched files match + if ( bInsertFileMacroExpansion ) + PerformFileSubstitions( ln, sizeof(lineBuffer) - (ln-lineBuffer) ); + + if ( memcmp( ln, "#include", 8 ) == 0 ) + { + // omg, an include + ln += 8; + ln += strspn( ln, " \t\"<" ); // skip whitespace, ", and < + + size_t nPathNameLength = strcspn( ln, " \t\">\n" ); + if ( !nPathNameLength ) + { + Sys_Error( "bad include %s via %s\n", lineBuffer, filename ); + } + ln[nPathNameLength] = 0; // kill everything after end of filename + + FILE *inchandle = fopen( ln, "r" ); + if ( !inchandle ) + { + Sys_Error( "can't open #include of %s\n", ln ); + } + if ( !nSP ) + { + Sys_Error( "include nesting too deep via %s", filename ); + } + pFileStack[--nSP] = inchandle; + } + else + { + size_t nLen = strlen( ln ); + nTotalFileBytes += nLen; + StringNode_t *pNewLine = MakeStrNode( ln ); + + pNewLine->m_pNext = pFileLines; + pFileLines = pNewLine; + } + } + fclose( pFileStack[nSP] ); + nSP++; // pop stack + } + + + // Reverse the pFileLines list so it goes the right way. + StringNode_t *pPrev = NULL; + StringNode_t *pCur; + for( pCur = pFileLines; pCur; ) + { + StringNode_t *pNext = pCur->m_pNext; + pCur->m_pNext = pPrev; + pPrev = pCur; + pCur = pNext; + } + pFileLines = pPrev; + + + // Now dump all the lines out into a single buffer. + char *buffer = new char[nTotalFileBytes + 1]; // and null + *bufferptr = buffer; // tell caller + + // copy all strings and null terminate + int nLine = 0; + StringNode_t *pNext; + for( pCur=pFileLines; pCur; pCur=pNext ) + { + pNext = pCur->m_pNext; + size_t nLen = strlen( pCur->m_Text ); + memcpy( buffer, pCur->m_Text, nLen ); + buffer += nLen; + nLine++; + + // Cleanup the line.. + //delete [] (unsigned char*)pCur; + } + *( buffer++ ) = 0; // null + + return (int)nTotalFileBytes; +} + + +// Just like fgets() but it removes trailing newlines. +char* ChompLineFromFile( char *pOut, int nOutBytes, FILE *fp ) +{ + char *pReturn = fgets( pOut, nOutBytes, fp ); + if ( pReturn ) + { + int len = (int)strlen( pReturn ); + if ( len > 0 && pReturn[len-1] == '\n' ) + { + pReturn[len-1] = 0; + if ( len > 1 && pReturn[len-2] == '\r' ) + pReturn[len-2] = 0; + } + } + + return pReturn; +} + + +bool CheckSupplementalString( const char *pSupplementalString, const char *pReferenceSupplementalString ) +{ + // The supplemental string is only checked while VPC is determining if a project file is stale or not. + // It's not used by the pre-build event's CRC check. + // The supplemental string contains various options that tell how the project was built. It's generated in VPC_GenerateCRCOptionString. + // + // If there's no reference supplemental string (which is the case if we're running vpccrccheck.exe), then we ignore it and continue. + if ( !pReferenceSupplementalString ) + return true; + + return ( pSupplementalString && pReferenceSupplementalString && stricmp( pSupplementalString, pReferenceSupplementalString ) == 0 ); +} + +bool CheckVPCExeCRC( char *pVPCCRCCheckString, const char *szFilename, char *pErrorString, int nErrorStringLength ) +{ + if ( pVPCCRCCheckString == NULL ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Unexpected end-of-file in %s", szFilename ); + return false; + } + + char *pSpace = strchr( pVPCCRCCheckString, ' ' ); + if ( !pSpace ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Invalid line ('%s') in %s", pVPCCRCCheckString, szFilename ); + return false; + } + + // Null-terminate it so we have the CRC by itself and the filename follows the space. + *pSpace = 0; + const char *pVPCFilename = pSpace + 1; + + // Parse the CRC out. + unsigned int nReferenceCRC; + sscanf( pVPCCRCCheckString, "%x", &nReferenceCRC ); + + char *pBuffer; + int cbVPCExe = Sys_LoadFile( pVPCFilename, (void**)&pBuffer ); + if ( !pBuffer ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s for comparison.", pVPCFilename ); + return false; + } + + if ( cbVPCExe < 0 ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Could not load file '%s' to check CRC", pVPCFilename ); + return false; + } + + // Calculate the CRC from the contents of the file. + CRC32_t nCRCFromFileContents = CRC32_ProcessSingleBuffer( pBuffer, cbVPCExe ); + delete [] pBuffer; + + // Compare them. + if ( nCRCFromFileContents != nReferenceCRC ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "VPC executable has changed since the project was generated." ); + return false; + } + return true; +} + + +bool VPC_CheckProjectDependencyCRCs( const char *pProjectFilename, const char *pReferenceSupplementalString, char *pErrorString, int nErrorStringLength ) +{ + // Build the xxxxx.vcproj.vpc_crc filename + char szFilename[512]; + SafeSnprintf( szFilename, sizeof( szFilename ), "%s.%s", pProjectFilename, VPCCRCCHECK_FILE_EXTENSION ); + + // Open it up. + FILE *fp = fopen( szFilename, "rt" ); + if ( !fp ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s to check CRC strings", szFilename ); + return false; + } + + bool bReturnValue = false; + char lineBuffer[2048]; + + // Check the version of the CRC file. + const char *pVersionString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp ); + if ( pVersionString && stricmp( pVersionString, VPCCRCCHECK_FILE_VERSION_STRING ) == 0 ) + { + char *pVPCExeCRCString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp ); + if ( CheckVPCExeCRC( pVPCExeCRCString, szFilename, pErrorString, nErrorStringLength ) ) + { + // Check the supplemental CRC string. + const char *pSupplementalString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp ); + if ( CheckSupplementalString( pSupplementalString, pReferenceSupplementalString ) ) + { + // Now read each line. Each line has a CRC and a filename on it. + while ( 1 ) + { + char *pLine = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp ); + if ( !pLine ) + { + // We got all the way through the file without a CRC error, so all's well. + bReturnValue = true; + break; + } + + char *pSpace = strchr( pLine, ' ' ); + if ( !pSpace ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Invalid line ('%s') in %s", pLine, szFilename ); + break; + } + + // Null-terminate it so we have the CRC by itself and the filename follows the space. + *pSpace = 0; + const char *pVPCFilename = pSpace + 1; + + // Parse the CRC out. + unsigned int nReferenceCRC; + sscanf( pLine, "%x", &nReferenceCRC ); + + + // Calculate the CRC from the contents of the file. + char *pBuffer; + int nTotalFileBytes = Sys_LoadTextFileWithIncludes( pVPCFilename, &pBuffer, true ); + if ( nTotalFileBytes == -1 ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s for CRC comparison.", pVPCFilename ); + break; + } + + CRC32_t nCRCFromTextContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes ); + delete [] pBuffer; + + // Compare them. + if ( nCRCFromTextContents != nReferenceCRC ) + { + SafeSnprintf( pErrorString, nErrorStringLength, "This VCPROJ is out of sync with its VPC scripts.\n %s mismatches (0x%x vs 0x%x).\n Please use VPC to re-generate!\n \n", pVPCFilename, nReferenceCRC, nCRCFromTextContents ); + break; + } + } + } + else + { + SafeSnprintf( pErrorString, nErrorStringLength, "Supplemental string mismatch." ); + } + } + } + else + { + SafeSnprintf( pErrorString, nErrorStringLength, "CRC file %s has an invalid version string ('%s')", szFilename, pVersionString ? pVersionString : "[null]" ); + } + + fclose( fp ); + return bReturnValue; +} + + +int VPC_OldeStyleCRCChecks( int argc, char **argv ) +{ + for ( int i=1; (i+2) < argc; ) + { + const char *pTestArg = argv[i]; + if ( stricmp( pTestArg, "-crc" ) != 0 ) + { + ++i; + continue; + } + + const char *pVPCFilename = argv[i+1]; + + // Get the CRC value on the command line. + const char *pTestCRC = argv[i+2]; + unsigned int nCRCFromCommandLine; + sscanf( pTestCRC, "%x", &nCRCFromCommandLine ); + + // Calculate the CRC from the contents of the file. + char *pBuffer; + int nTotalFileBytes = Sys_LoadTextFileWithIncludes( pVPCFilename, &pBuffer, true ); + if ( nTotalFileBytes == -1 ) + { + Sys_Error( "Unable to load %s for CRC comparison.", pVPCFilename ); + } + + CRC32_t nCRCFromTextContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes ); + delete [] pBuffer; + + // Compare them. + if ( nCRCFromTextContents != nCRCFromCommandLine ) + { + Sys_Error( " \n This VCPROJ is out of sync with its VPC scripts.\n %s mismatches (0x%x vs 0x%x).\n Please use VPC to re-generate!\n \n", pVPCFilename, nCRCFromCommandLine, nCRCFromTextContents ); + } + + i += 2; + } + + return 0; +} + + +int VPC_CommandLineCRCChecks( int argc, char **argv ) +{ + if ( argc < 2 ) + { + fprintf( stderr, "Invalid arguments to " VPCCRCCHECK_EXE_FILENAME ". Format: " VPCCRCCHECK_EXE_FILENAME " [project filename]\n" ); + return 1; + } + + const char *pFirstCRC = argv[1]; + + // If the first argument starts with -crc but is not -crc2, then this is an old CRC check command line with all the CRCs and filenames + // directly on the command line. The new format puts all that in a separate file. + if ( pFirstCRC[0] == '-' && pFirstCRC[1] == 'c' && pFirstCRC[2] == 'r' && pFirstCRC[3] == 'c' && pFirstCRC[4] != '2' ) + { + return VPC_OldeStyleCRCChecks( argc, argv ); + } + + if ( stricmp( pFirstCRC, "-crc2" ) != 0 ) + { + fprintf( stderr, "Missing -crc2 parameter on vpc CRC check command line." ); + return 1; + } + + const char *pProjectFilename = argv[2]; + + char errorString[1024]; + bool bCRCsValid = VPC_CheckProjectDependencyCRCs( pProjectFilename, NULL, errorString, sizeof( errorString ) ); + + if ( bCRCsValid ) + { + return 0; + } + else + { + fprintf( stderr, "%s", errorString ); + return 1; + } +} + diff --git a/external/vpc/utils/vpccrccheck/crccheck_shared.h b/external/vpc/utils/vpccrccheck/crccheck_shared.h new file mode 100644 index 0000000..ce177d0 --- /dev/null +++ b/external/vpc/utils/vpccrccheck/crccheck_shared.h @@ -0,0 +1,34 @@ +//===================== Copyright (c) Valve Corporation. All Rights Reserved. ====================== +// +// +// +//================================================================================================== + +#ifndef CRCCHECK_SHARED_H +#define CRCCHECK_SHARED_H +#ifdef _WIN32 +#pragma once +#endif + + +#ifdef STANDALONE_VPC +#define VPCCRCCHECK_EXE_FILENAME "vpc.exe" +#else +#define VPCCRCCHECK_EXE_FILENAME "vpccrccheck.exe" +#endif + +// The file extension for the file that contains the CRCs that a vcproj depends on. +#define VPCCRCCHECK_FILE_EXTENSION "vpc_crc" +#define VPCCRCCHECK_FILE_VERSION_STRING "[vpc crc file version 2]" + + +void Sys_Error( const char *format, ... ); +int Sys_LoadTextFileWithIncludes( const char* filename, char** bufferptr, bool bInsertFileMacroExpansion ); + +bool VPC_CheckProjectDependencyCRCs( const char *pProjectFilename, const char *pReferenceSupplementalString, char *pErrorString, int nErrorStringLength ); + +// Used by vpccrccheck.exe or by vpc.exe to do the CRC check that's initiated in the pre-build steps. +int VPC_CommandLineCRCChecks( int argc, char **argv ); + + +#endif // CRCCHECK_SHARED_H diff --git a/external/vpc/utils/vpccrccheck/vpccrccheck.cpp b/external/vpc/utils/vpccrccheck/vpccrccheck.cpp new file mode 100644 index 0000000..28b5667 --- /dev/null +++ b/external/vpc/utils/vpccrccheck/vpccrccheck.cpp @@ -0,0 +1,16 @@ + +#include "tier1/checksum_crc.h" +#include "crccheck_shared.h" +#include <stdio.h> +#include <string.h> + + + +int main( int argc, char **argv ) +{ + return VPC_CommandLineCRCChecks( argc, argv ); +} + + + + diff --git a/external/vpc/utils/vpccrccheck/vpccrccheck.vpc b/external/vpc/utils/vpccrccheck/vpccrccheck.vpc new file mode 100644 index 0000000..6cdfced --- /dev/null +++ b/external/vpc/utils/vpccrccheck/vpccrccheck.vpc @@ -0,0 +1,33 @@ + //----------------------------------------------------------------------------- +// VPCCRCCHECK.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\devtools\bin" + +$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc" + +$Configuration +{ + $Compiler + { + } +} + +$Project "vpccrccheck" +{ + $Folder "Source Files" + { + -$File "$SRCDIR\public\tier0\memoverride.cpp" + $File "vpccrccheck.cpp" + $File "crccheck_shared.cpp" + $File "$SRCDIR/tier1/checksum_crc.cpp" + } + + $Folder "Link Libraries" + { + -$Implib "$LIBPUBLIC\vstdlib" + } +} |