Team Fortress 2 Source Code as on 22/4/2020
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.

370 lines
9.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "crccheck_shared.h"
  3. #include "tier1/checksum_crc.h"
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <stdarg.h>
  7. #ifdef _WIN32
  8. #include <process.h>
  9. #else
  10. #include <stdlib.h>
  11. #define stricmp strcasecmp
  12. #endif
  13. #pragma warning( disable : 4996 )
  14. #pragma warning( disable : 4127 )
  15. #define MAX_INCLUDE_STACK_DEPTH 10
  16. #ifdef POSIX
  17. #define _vsnprintf vsnprintf
  18. #endif
  19. //-----------------------------------------------------------------------------
  20. // Sys_Error
  21. //
  22. //-----------------------------------------------------------------------------
  23. void Sys_Error( const char* format, ... )
  24. {
  25. va_list argptr;
  26. va_start( argptr,format );
  27. vfprintf( stderr, format, argptr );
  28. va_end( argptr );
  29. exit( 1 );
  30. }
  31. void SafeSnprintf( char *pOut, int nOutLen, const char *pFormat, ... )
  32. {
  33. va_list marker;
  34. va_start( marker, pFormat );
  35. _vsnprintf( pOut, nOutLen, pFormat, marker );
  36. va_end( marker );
  37. pOut[nOutLen-1] = 0;
  38. }
  39. // for linked lists of strings
  40. struct StringNode_t
  41. {
  42. StringNode_t *m_pNext;
  43. char m_Text[1]; // the string data
  44. };
  45. static StringNode_t *MakeStrNode( char const *pStr )
  46. {
  47. size_t nLen = strlen( pStr );
  48. StringNode_t *nRet = ( StringNode_t * ) new unsigned char[sizeof( StringNode_t ) + nLen ];
  49. strcpy( nRet->m_Text, pStr );
  50. return nRet;
  51. }
  52. //-----------------------------------------------------------------------------
  53. // Sys_LoadFile
  54. //-----------------------------------------------------------------------------
  55. int Sys_LoadTextFileWithIncludes( const char* filename, char** bufferptr )
  56. {
  57. FILE *pFileStack[MAX_INCLUDE_STACK_DEPTH];
  58. int nSP = MAX_INCLUDE_STACK_DEPTH;
  59. StringNode_t *pFileLines = NULL; // tail ptr for fast adds
  60. size_t nTotalFileBytes = 0;
  61. FILE *handle = fopen( filename, "r" );
  62. if ( !handle )
  63. return -1;
  64. pFileStack[--nSP] = handle; // push
  65. while ( nSP < MAX_INCLUDE_STACK_DEPTH )
  66. {
  67. // read lines
  68. for (;;)
  69. {
  70. char lineBuffer[2048];
  71. char *ln = fgets( lineBuffer, sizeof( lineBuffer ), pFileStack[nSP] );
  72. if ( !ln )
  73. break; // out of text
  74. ln += strspn( ln, "\t " ); // skip white space
  75. if ( memcmp( ln, "#include", 8 ) == 0 )
  76. {
  77. // omg, an include
  78. ln += 8;
  79. ln += strspn( ln, " \t\"<" ); // skip whitespace, ", and <
  80. size_t nPathNameLength = strcspn( ln, " \t\">\n" );
  81. if ( !nPathNameLength )
  82. {
  83. Sys_Error( "bad include %s via %s\n", lineBuffer, filename );
  84. }
  85. ln[nPathNameLength] = 0; // kill everything after end of filename
  86. FILE *inchandle = fopen( ln, "r" );
  87. if ( !inchandle )
  88. {
  89. Sys_Error( "can't open #include of %s\n", ln );
  90. }
  91. if ( !nSP )
  92. {
  93. Sys_Error( "include nesting too deep via %s", filename );
  94. }
  95. pFileStack[--nSP] = inchandle;
  96. }
  97. else
  98. {
  99. size_t nLen = strlen( ln );
  100. nTotalFileBytes += nLen;
  101. StringNode_t *pNewLine = MakeStrNode( ln );
  102. pNewLine->m_pNext = pFileLines;
  103. pFileLines = pNewLine;
  104. }
  105. }
  106. fclose( pFileStack[nSP] );
  107. nSP++; // pop stack
  108. }
  109. // Reverse the pFileLines list so it goes the right way.
  110. StringNode_t *pPrev = NULL;
  111. StringNode_t *pCur;
  112. for( pCur = pFileLines; pCur; )
  113. {
  114. StringNode_t *pNext = pCur->m_pNext;
  115. pCur->m_pNext = pPrev;
  116. pPrev = pCur;
  117. pCur = pNext;
  118. }
  119. pFileLines = pPrev;
  120. // Now dump all the lines out into a single buffer.
  121. char *buffer = new char[nTotalFileBytes + 1]; // and null
  122. *bufferptr = buffer; // tell caller
  123. // copy all strings and null terminate
  124. int nLine = 0;
  125. StringNode_t *pNext;
  126. for( pCur=pFileLines; pCur; pCur=pNext )
  127. {
  128. pNext = pCur->m_pNext;
  129. size_t nLen = strlen( pCur->m_Text );
  130. memcpy( buffer, pCur->m_Text, nLen );
  131. buffer += nLen;
  132. nLine++;
  133. // Cleanup the line..
  134. //delete [] (unsigned char*)pCur;
  135. }
  136. *( buffer++ ) = 0; // null
  137. return (int)nTotalFileBytes;
  138. }
  139. // Just like fgets() but it removes trailing newlines.
  140. char* ChompLineFromFile( char *pOut, int nOutBytes, FILE *fp )
  141. {
  142. char *pReturn = fgets( pOut, nOutBytes, fp );
  143. if ( pReturn )
  144. {
  145. int len = (int)strlen( pReturn );
  146. if ( len > 0 && pReturn[len-1] == '\n' )
  147. {
  148. pReturn[len-1] = 0;
  149. if ( len > 1 && pReturn[len-2] == '\r' )
  150. pReturn[len-2] = 0;
  151. }
  152. }
  153. return pReturn;
  154. }
  155. bool CheckSupplementalString( const char *pSupplementalString, const char *pReferenceSupplementalString )
  156. {
  157. // The supplemental string is only checked while VPC is determining if a project file is stale or not.
  158. // It's not used by the pre-build event's CRC check.
  159. // The supplemental string contains various options that tell how the project was built. It's generated in VPC_GenerateCRCOptionString.
  160. //
  161. // If there's no reference supplemental string (which is the case if we're running vpccrccheck.exe), then we ignore it and continue.
  162. if ( !pReferenceSupplementalString )
  163. return true;
  164. return ( pSupplementalString && pReferenceSupplementalString && stricmp( pSupplementalString, pReferenceSupplementalString ) == 0 );
  165. }
  166. bool VPC_CheckProjectDependencyCRCs( const char *pProjectFilename, const char *pReferenceSupplementalString, char *pErrorString, int nErrorStringLength )
  167. {
  168. // Build the xxxxx.vcproj.vpc_crc filename
  169. char szFilename[512];
  170. SafeSnprintf( szFilename, sizeof( szFilename ), "%s.%s", pProjectFilename, VPCCRCCHECK_FILE_EXTENSION );
  171. // Open it up.
  172. FILE *fp = fopen( szFilename, "rt" );
  173. if ( !fp )
  174. {
  175. SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s to check CRC strings", szFilename );
  176. return false;
  177. }
  178. bool bReturnValue = false;
  179. char lineBuffer[2048];
  180. // Check the version of the CRC file.
  181. const char *pVersionString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  182. if ( pVersionString && stricmp( pVersionString, VPCCRCCHECK_FILE_VERSION_STRING ) == 0 )
  183. {
  184. // Check the supplemental CRC string.
  185. const char *pSupplementalString = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  186. if ( CheckSupplementalString( pSupplementalString, pReferenceSupplementalString ) )
  187. {
  188. // Now read each line. Each line has a CRC and a filename on it.
  189. while ( 1 )
  190. {
  191. char *pLine = ChompLineFromFile( lineBuffer, sizeof( lineBuffer ), fp );
  192. if ( !pLine )
  193. {
  194. // We got all the way through the file without a CRC error, so all's well.
  195. bReturnValue = true;
  196. break;
  197. }
  198. char *pSpace = strchr( pLine, ' ' );
  199. if ( !pSpace )
  200. {
  201. SafeSnprintf( pErrorString, nErrorStringLength, "Invalid line ('%s') in %s", pLine, szFilename );
  202. break;
  203. }
  204. // Null-terminate it so we have the CRC by itself and the filename follows the space.
  205. *pSpace = 0;
  206. const char *pVPCFilename = pSpace + 1;
  207. // Parse the CRC out.
  208. unsigned int nReferenceCRC;
  209. sscanf( pLine, "%x", &nReferenceCRC );
  210. // Calculate the CRC from the contents of the file.
  211. char *pBuffer;
  212. int nTotalFileBytes = Sys_LoadTextFileWithIncludes( pVPCFilename, &pBuffer );
  213. if ( nTotalFileBytes == -1 )
  214. {
  215. SafeSnprintf( pErrorString, nErrorStringLength, "Unable to load %s for CRC comparison.", pVPCFilename );
  216. break;
  217. }
  218. CRC32_t nCRCFromTextContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes );
  219. delete [] pBuffer;
  220. // Compare them.
  221. if ( nCRCFromTextContents != nReferenceCRC )
  222. {
  223. 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 );
  224. break;
  225. }
  226. }
  227. }
  228. else
  229. {
  230. SafeSnprintf( pErrorString, nErrorStringLength, "Supplemental string mismatch." );
  231. }
  232. }
  233. else
  234. {
  235. SafeSnprintf( pErrorString, nErrorStringLength, "CRC file %s has an invalid version string ('%s')", szFilename, pVersionString ? pVersionString : "[null]" );
  236. }
  237. fclose( fp );
  238. return bReturnValue;
  239. }
  240. int VPC_OldeStyleCRCChecks( int argc, char **argv )
  241. {
  242. for ( int i=1; (i+2) < argc; )
  243. {
  244. const char *pTestArg = argv[i];
  245. if ( stricmp( pTestArg, "-crc" ) != 0 )
  246. {
  247. ++i;
  248. continue;
  249. }
  250. const char *pVPCFilename = argv[i+1];
  251. // Get the CRC value on the command line.
  252. const char *pTestCRC = argv[i+2];
  253. unsigned int nCRCFromCommandLine;
  254. sscanf( pTestCRC, "%x", &nCRCFromCommandLine );
  255. // Calculate the CRC from the contents of the file.
  256. char *pBuffer;
  257. int nTotalFileBytes = Sys_LoadTextFileWithIncludes( pVPCFilename, &pBuffer );
  258. if ( nTotalFileBytes == -1 )
  259. {
  260. Sys_Error( "Unable to load %s for CRC comparison.", pVPCFilename );
  261. }
  262. CRC32_t nCRCFromTextContents = CRC32_ProcessSingleBuffer( pBuffer, nTotalFileBytes );
  263. delete [] pBuffer;
  264. // Compare them.
  265. if ( nCRCFromTextContents != nCRCFromCommandLine )
  266. {
  267. 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 );
  268. }
  269. i += 2;
  270. }
  271. return 0;
  272. }
  273. int VPC_CommandLineCRCChecks( int argc, char **argv )
  274. {
  275. if ( argc < 2 )
  276. {
  277. fprintf( stderr, "Invalid arguments to " VPCCRCCHECK_EXE_FILENAME ". Format: " VPCCRCCHECK_EXE_FILENAME " [project filename]\n" );
  278. return 1;
  279. }
  280. const char *pFirstCRC = argv[1];
  281. // 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
  282. // directly on the command line. The new format puts all that in a separate file.
  283. if ( pFirstCRC[0] == '-' && pFirstCRC[1] == 'c' && pFirstCRC[2] == 'r' && pFirstCRC[3] == 'c' && pFirstCRC[4] != '2' )
  284. {
  285. return VPC_OldeStyleCRCChecks( argc, argv );
  286. }
  287. if ( stricmp( pFirstCRC, "-crc2" ) != 0 )
  288. {
  289. fprintf( stderr, "Missing -crc2 parameter on vpc CRC check command line." );
  290. return 1;
  291. }
  292. const char *pProjectFilename = argv[2];
  293. char errorString[1024];
  294. bool bCRCsValid = VPC_CheckProjectDependencyCRCs( pProjectFilename, NULL, errorString, sizeof( errorString ) );
  295. if ( bCRCsValid )
  296. {
  297. return 0;
  298. }
  299. else
  300. {
  301. fprintf( stderr, "%s", errorString );
  302. return 1;
  303. }
  304. }