Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

469 lines
13 KiB

  1. #include "tier0/platform.h"
  2. #include "crccheck_shared.h"
  3. #include "tier1/checksum_crc.h"
  4. #include "tier1/strtools.h"
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include <stdarg.h>
  8. #ifdef _WIN32
  9. #define WIN32_LEAN_AND_MEAN
  10. #include <windows.h>
  11. #include <io.h>
  12. #include <fcntl.h>
  13. #undef _stricmp
  14. #define V_stricmp_fast _stricmp
  15. #include <process.h>
  16. #else
  17. #include <stdlib.h>
  18. // stricmp is defined to a Valve routine in platform.h
  19. // which currently we don't want to use because of
  20. // the no-dependency nature of this code.
  21. // When this folds back into vpc this should go away
  22. // and the platform.h definition should be used.
  23. #undef strcasecmp
  24. #define V_stricmp_fast strcasecmp
  25. #endif
  26. #if defined( POSIX )
  27. #include <fcntl.h>
  28. #include <sys/stat.h>
  29. #endif
  30. #pragma warning( disable : 4996 )
  31. #pragma warning( disable : 4127 )
  32. #define MAX_INCLUDE_STACK_DEPTH 10
  33. //-----------------------------------------------------------------------------
  34. // Sys_Error
  35. //
  36. //-----------------------------------------------------------------------------
  37. void Sys_Error( const char* format, ... )
  38. {
  39. va_list argptr;
  40. va_start( argptr,format );
  41. vfprintf( stderr, format, argptr );
  42. va_end( argptr );
  43. exit( 1 );
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Returns TRUE if file exists.
  47. //-----------------------------------------------------------------------------
  48. bool CRCCheck_FileExists( const char *filename )
  49. {
  50. FILE *test;
  51. if ( ( test = fopen( filename, "rb" ) ) == NULL )
  52. return ( false );
  53. fclose( test );
  54. return true;
  55. }
  56. int CRCCheck_LoadFile( const char *filename, void **bufferptr, bool bText )
  57. {
  58. int handle;
  59. long length;
  60. char* buffer;
  61. *bufferptr = NULL;
  62. if ( !CRCCheck_FileExists( filename ) )
  63. return ( -1 );
  64. int flags = _O_RDONLY;
  65. #if !defined( POSIX )
  66. flags |= (bText ? _O_TEXT : _O_BINARY);
  67. #endif
  68. handle = _open( filename, flags );
  69. if ( handle == -1 )
  70. Sys_Error( "CRCCheck_LoadFile(): Error opening %s: %s", filename, strerror( errno ) );
  71. length = _lseek( handle, 0, SEEK_END );
  72. _lseek( handle, 0, SEEK_SET );
  73. buffer = new char[length + 1];
  74. int bytesRead = _read( handle, buffer, length );
  75. if ( !bText && ( bytesRead != length ) )
  76. Sys_Error( "CRCCheck_LoadFile(): read truncated failure" );
  77. _close( handle );
  78. // text mode is truncated, add null for parsing
  79. buffer[bytesRead] = '\0';
  80. *bufferptr = ( void* )buffer;
  81. return ( bytesRead );
  82. }
  83. void SafeSnprintf( char *pOut, int nOutLen, const char *pFormat, ... )
  84. {
  85. va_list marker;
  86. va_start( marker, pFormat );
  87. _vsnprintf( pOut, nOutLen, pFormat, marker );
  88. va_end( marker );
  89. pOut[nOutLen-1] = 0;
  90. }
  91. void FixSlashes( const char *pIn, char *pOut, int nOutLen )
  92. {
  93. if ( nOutLen <= 0 )
  94. return;
  95. strncpy( pOut, pIn, nOutLen - 1 );
  96. pOut[nOutLen - 1] = '\0';
  97. int nCount = (int)strlen( pOut );
  98. for ( int i = 0; i < nCount; i++ )
  99. {
  100. if ( ( pOut[i] == '\\' || pOut[i] == '/' ) && pOut[i] != CORRECT_PATH_SEPARATOR )
  101. {
  102. pOut[i] = CORRECT_PATH_SEPARATOR;
  103. }
  104. }
  105. }
  106. // for linked lists of strings
  107. struct StringNode_t
  108. {
  109. StringNode_t *m_pNext;
  110. char m_Text[1]; // the string data
  111. };
  112. static StringNode_t *MakeStrNode( char const *pStr )
  113. {
  114. size_t nLen = strlen( pStr );
  115. StringNode_t *nRet = ( StringNode_t * ) new unsigned char[sizeof( StringNode_t ) + nLen ];
  116. strcpy( nRet->m_Text, pStr );
  117. return nRet;
  118. }
  119. //-----------------------------------------------------------------------------
  120. //-----------------------------------------------------------------------------
  121. int Sys_LoadTextFileWithIncludes( const char* filename, char** bufferptr, bool bInsertFileMacroExpansion )
  122. {
  123. FILE *pFileStack[MAX_INCLUDE_STACK_DEPTH];
  124. int nSP = MAX_INCLUDE_STACK_DEPTH;
  125. StringNode_t *pFileLines = NULL; // tail ptr for fast adds
  126. size_t nTotalFileBytes = 0;
  127. FILE *handle = fopen( filename, "r" );
  128. if ( !handle )
  129. return -1;
  130. pFileStack[--nSP] = handle; // push
  131. while ( nSP < MAX_INCLUDE_STACK_DEPTH )
  132. {
  133. // read lines
  134. for (;;)
  135. {
  136. char lineBuffer[4096];
  137. char *ln = fgets( lineBuffer, sizeof( lineBuffer ), pFileStack[nSP] );
  138. if ( !ln )
  139. break; // out of text
  140. ln += strspn( ln, "\t " ); // skip white space
  141. if ( memcmp( ln, "#include", 8 ) == 0 )
  142. {
  143. // omg, an include
  144. ln += 8;
  145. ln += strspn( ln, " \t\"<" ); // skip whitespace, ", and <
  146. size_t nPathNameLength = strcspn( ln, " \t\">\n" );
  147. if ( !nPathNameLength )
  148. {
  149. Sys_Error( "bad include %s via %s\n", lineBuffer, filename );
  150. }
  151. ln[nPathNameLength] = 0; // kill everything after end of filename
  152. char fixedIncludePath[512];
  153. FixSlashes( ln, fixedIncludePath, sizeof( fixedIncludePath ) );
  154. FILE *inchandle = fopen( fixedIncludePath, "r" );
  155. if ( !inchandle )
  156. {
  157. Sys_Error( "can't open #include of %s\n", fixedIncludePath );
  158. }
  159. if ( !nSP )
  160. {
  161. Sys_Error( "include nesting too deep via %s", filename );
  162. }
  163. pFileStack[--nSP] = inchandle;
  164. }
  165. else
  166. {
  167. size_t nLen = strlen( ln );
  168. nTotalFileBytes += nLen;
  169. StringNode_t *pNewLine = MakeStrNode( ln );
  170. pNewLine->m_pNext = pFileLines;
  171. pFileLines = pNewLine;
  172. }
  173. }
  174. fclose( pFileStack[nSP] );
  175. nSP++; // pop stack
  176. }
  177. // Reverse the pFileLines list so it goes the right way.
  178. StringNode_t *pPrev = NULL;
  179. StringNode_t *pCur;
  180. for( pCur = pFileLines; pCur; )
  181. {
  182. StringNode_t *pNext = pCur->m_pNext;
  183. pCur->m_pNext = pPrev;
  184. pPrev = pCur;
  185. pCur = pNext;
  186. }
  187. pFileLines = pPrev;
  188. // Now dump all the lines out into a single buffer.
  189. char *buffer = new char[nTotalFileBytes + 1]; // and null
  190. *bufferptr = buffer; // tell caller
  191. // copy all strings and null terminate
  192. int nLine = 0;
  193. StringNode_t *pNext;
  194. for( pCur=pFileLines; pCur; pCur=pNext )
  195. {
  196. pNext = pCur->m_pNext;
  197. size_t nLen = strlen( pCur->m_Text );
  198. memcpy( buffer, pCur->m_Text, nLen );
  199. buffer += nLen;
  200. nLine++;
  201. // Cleanup the line..
  202. delete [] (unsigned char*)pCur;
  203. }
  204. *( buffer++ ) = 0; // null
  205. return (int)nTotalFileBytes;
  206. }
  207. // Just like fgets() but it removes trailing newlines.
  208. char* ChompLineFromFile( char *pOut, int nOutBytes, FILE *fp )
  209. {
  210. char *pReturn = fgets( pOut, nOutBytes, fp );
  211. if ( pReturn )
  212. {
  213. int len = (int)strlen( pReturn );
  214. if ( len > 0 && pReturn[len-1] == '\n' )
  215. {
  216. pReturn[len-1] = 0;
  217. if ( len > 1 && pReturn[len-2] == '\r' )
  218. pReturn[len-2] = 0;
  219. }
  220. }
  221. return pReturn;
  222. }
  223. bool CheckSupplementalString( const char *pSupplementalString, const char *pReferenceSupplementalString )
  224. {
  225. // The supplemental string is only checked while VPC is determining if a project file is stale or not.
  226. // It's not used by the pre-build event's CRC check.
  227. // The supplemental string contains various options that tell how the project was built. It's generated in VPC_GenerateCRCOptionString.
  228. //
  229. // If there's no reference supplemental string (which is the case if we're running vpccrccheck.exe), then we ignore it and continue.
  230. if ( !pReferenceSupplementalString )
  231. return true;
  232. return ( pSupplementalString && pReferenceSupplementalString && V_stricmp_fast( pSupplementalString, pReferenceSupplementalString ) == 0 );
  233. }
  234. bool VPC_CheckProjectDependencyCRCs( const char *szCRCFile, const char *pReferenceSupplementalString, char *pErrorString, int nErrorStringLength )
  235. {
  236. // Open it up.
  237. FILE *fp = fopen( szCRCFile, "rt" );
  238. if ( !fp )
  239. {
  240. SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s to check CRC strings", szCRCFile );
  241. return false;
  242. }
  243. bool bReturnValue = false;
  244. char lineBuffer[2048];
  245. // Check the version of the CRC file.
  246. const char *pVersionString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  247. if ( pVersionString && V_stricmp_fast( pVersionString, VPCCRCCHECK_FILE_VERSION_STRING ) == 0 )
  248. {
  249. // Check the supplemental CRC string.
  250. const char *pSupplementalString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  251. if ( CheckSupplementalString( pSupplementalString, pReferenceSupplementalString ) )
  252. {
  253. // Skip over one line of additional metadata used by the VS add-in
  254. ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  255. // Now read each line. Each line has a CRC and a filename on it.
  256. while ( 1 )
  257. {
  258. char *pLine = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  259. if ( !pLine )
  260. {
  261. // We got all the way through the file without a CRC error, so all's well.
  262. bReturnValue = true;
  263. break;
  264. }
  265. // resolve type of file to CRC (binary or text script with includes)
  266. bool bFileIsBinary = false;
  267. if ( !strncmp( lineBuffer, "BIN ", 4 ) )
  268. {
  269. // file is binary
  270. bFileIsBinary = true;
  271. // move past the tag
  272. pLine += 4;
  273. }
  274. // expecting <crc> <filename>
  275. char *pSpace = strchr( pLine, ' ' );
  276. if ( !pSpace )
  277. {
  278. SafeSnprintf( pErrorString, nErrorStringLength, "Invalid line ('%s') in %s", pLine, szCRCFile );
  279. break;
  280. }
  281. // Null-terminate it so we have the CRC by itself and the filename follows the space.
  282. *pSpace = 0;
  283. const char *pVPCFilename = pSpace + 1;
  284. char fixedVPCFilename[512];
  285. FixSlashes( pVPCFilename, fixedVPCFilename, sizeof( fixedVPCFilename ) );
  286. // Parse the CRC out.
  287. unsigned int nReferenceCRC;
  288. sscanf( pLine, "%x", &nReferenceCRC );
  289. // Calculate the CRC from the contents of the file.
  290. char *pBuffer = NULL;
  291. int nTotalFileBytes = CRCCheck_LoadFile( fixedVPCFilename, (void**)&pBuffer, !bFileIsBinary );
  292. if ( nTotalFileBytes < 0 )
  293. {
  294. SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s for CRC comparison.", fixedVPCFilename );
  295. break;
  296. }
  297. CRC32_t nCRCFromContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes );
  298. delete [] pBuffer;
  299. // Compare them.
  300. if ( nCRCFromContents != nReferenceCRC )
  301. {
  302. SafeSnprintf( pErrorString, nErrorStringLength, "This VCXPROJ 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", fixedVPCFilename, nReferenceCRC, nCRCFromContents );
  303. break;
  304. }
  305. }
  306. }
  307. else
  308. {
  309. SafeSnprintf( pErrorString, nErrorStringLength, "Supplemental string mismatch." );
  310. }
  311. }
  312. else
  313. {
  314. SafeSnprintf( pErrorString, nErrorStringLength, "CRC file %s has an invalid version string ('%s')", szCRCFile, pVersionString ? pVersionString : "[null]" );
  315. }
  316. fclose( fp );
  317. return bReturnValue;
  318. }
  319. int VPC_OldeStyleCRCChecks( int argc, char **argv )
  320. {
  321. for ( int i=1; (i+2) < argc; )
  322. {
  323. const char *pTestArg = argv[i];
  324. if ( V_stricmp_fast( pTestArg, "-crc" ) != 0 )
  325. {
  326. ++i;
  327. continue;
  328. }
  329. const char *pVPCFilename = argv[i+1];
  330. char fixedVPCFilename[512];
  331. FixSlashes( pVPCFilename, fixedVPCFilename, sizeof( fixedVPCFilename ) );
  332. // Get the CRC value on the command line.
  333. const char *pTestCRC = argv[i+2];
  334. unsigned int nCRCFromCommandLine;
  335. sscanf( pTestCRC, "%x", &nCRCFromCommandLine );
  336. // Calculate the CRC from the contents of the file.
  337. char *pBuffer;
  338. int nTotalFileBytes = Sys_LoadTextFileWithIncludes( fixedVPCFilename, &pBuffer, true );
  339. if ( nTotalFileBytes == -1 )
  340. {
  341. Sys_Error( "Unable to load %s for CRC comparison.", fixedVPCFilename );
  342. }
  343. CRC32_t nCRCFromTextContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes );
  344. delete [] pBuffer;
  345. // Compare them.
  346. if ( nCRCFromTextContents != nCRCFromCommandLine )
  347. {
  348. Sys_Error( " \n This VCXPROJ is out of sync with its VPC scripts.\n %s mismatches (0x%x vs 0x%x).\n Please use VPC to regenerate!\n \n", fixedVPCFilename, nCRCFromCommandLine, nCRCFromTextContents );
  349. }
  350. i += 2;
  351. }
  352. return 0;
  353. }
  354. int VPC_CommandLineCRCChecks( int argc, char **argv )
  355. {
  356. if ( argc < 2 )
  357. {
  358. fprintf( stderr, "%s (Build: %s %s)\n", VPCCRCCHECK_EXE_FILENAME, __DATE__, __TIME__ );
  359. fprintf( stderr, "Invalid arguments to " VPCCRCCHECK_EXE_FILENAME ". Format: " VPCCRCCHECK_EXE_FILENAME " [project or script filename]\n" );
  360. return 1;
  361. }
  362. const char *pFirstCRC = argv[1];
  363. // 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
  364. // directly on the command line. The new format puts all that in a separate file.
  365. if ( pFirstCRC[0] == '-' && pFirstCRC[1] == 'c' && pFirstCRC[2] == 'r' && pFirstCRC[3] == 'c' && pFirstCRC[4] != '2' )
  366. {
  367. return VPC_OldeStyleCRCChecks( argc, argv );
  368. }
  369. if ( V_stricmp_fast( pFirstCRC, "-crc2" ) != 0 )
  370. {
  371. fprintf( stderr, "Missing -crc2 parameter on vpc CRC check command line." );
  372. return 1;
  373. }
  374. const char *pProjectFilename = argv[2];
  375. // Build the xxxxx.vcproj.vpc_crc filename
  376. char szCRCFilename[512];
  377. SafeSnprintf( szCRCFilename, sizeof( szCRCFilename ), "%s.%s", pProjectFilename, VPCCRCCHECK_FILE_EXTENSION );
  378. char errorString[1024];
  379. bool bCRCsValid = VPC_CheckProjectDependencyCRCs( szCRCFilename, NULL, errorString, sizeof( errorString ) );
  380. if ( bCRCsValid )
  381. {
  382. return 0;
  383. }
  384. else
  385. {
  386. fprintf( stderr, "%s", errorString );
  387. return 1;
  388. }
  389. }