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.

438 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include "vtf/vtf.h"
  13. #include "tier1/UtlBuffer.h"
  14. #include "tier1/utlmap.h"
  15. #include "bitmap/imageformat.h"
  16. #include "mathlib/vector.h"
  17. #include <conio.h>
  18. void Usage( void )
  19. {
  20. printf( "Usage: vtfdiff file1.vtf file2.vtf\n" );
  21. }
  22. bool LoadFileIntoBuffer( const char *pFileName, CUtlBuffer &buf )
  23. {
  24. struct _stat statBuf;
  25. if( _stat( pFileName, &statBuf ) != 0 )
  26. {
  27. goto error;
  28. }
  29. buf.EnsureCapacity( statBuf.st_size );
  30. FILE *fp;
  31. fp = fopen( pFileName, "rb" );
  32. if( !fp )
  33. {
  34. goto error;
  35. }
  36. int nBytesRead = fread( buf.Base(), 1, statBuf.st_size, fp );
  37. fclose( fp );
  38. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  39. return true;
  40. error:
  41. printf( "Can't find file %s\n", pFileName );
  42. return false;
  43. }
  44. char const * ResourceToString( uint32 uiResType )
  45. {
  46. static char chBuffer[256];
  47. switch ( uiResType )
  48. {
  49. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  50. return "VTF_LEGACY_RSRC_LOW_RES_IMAGE";
  51. case VTF_LEGACY_RSRC_IMAGE:
  52. return "VTF_LEGACY_RSRC_IMAGE";
  53. case VTF_RSRC_SHEET:
  54. return "VTF_RSRC_SHEET";
  55. case MK_VTF_RSRC_ID( 'C','R','C' ):
  56. return "CRC";
  57. case VTF_RSRC_TEXTURE_LOD_SETTINGS:
  58. return "VTF_RSRC_TEXTURE_LOD_SETTINGS";
  59. default:
  60. sprintf( chBuffer, "0x%08X", uiResType );
  61. return chBuffer;
  62. }
  63. return chBuffer;
  64. }
  65. void PrintFlags( int flags )
  66. {
  67. #define PRNFLAG( flagname ) \
  68. if ( ( flags & (flagname) ) == (flagname) ) \
  69. { \
  70. flags &=~ (flagname); \
  71. printf( "%s%s", #flagname + strlen("TEXTUREFLAGS_"), flags ? "|" : "" ); \
  72. } \
  73. PRNFLAG( TEXTUREFLAGS_POINTSAMPLE )
  74. PRNFLAG( TEXTUREFLAGS_TRILINEAR )
  75. PRNFLAG( TEXTUREFLAGS_CLAMPS )
  76. PRNFLAG( TEXTUREFLAGS_CLAMPT )
  77. PRNFLAG( TEXTUREFLAGS_ANISOTROPIC )
  78. PRNFLAG( TEXTUREFLAGS_HINT_DXT5 )
  79. PRNFLAG( TEXTUREFLAGS_SRGB )
  80. PRNFLAG( TEXTUREFLAGS_NORMAL )
  81. PRNFLAG( TEXTUREFLAGS_NOMIP )
  82. PRNFLAG( TEXTUREFLAGS_NOLOD )
  83. PRNFLAG( TEXTUREFLAGS_ALL_MIPS )
  84. PRNFLAG( TEXTUREFLAGS_PROCEDURAL )
  85. PRNFLAG( TEXTUREFLAGS_ONEBITALPHA )
  86. PRNFLAG( TEXTUREFLAGS_EIGHTBITALPHA )
  87. PRNFLAG( TEXTUREFLAGS_ENVMAP )
  88. PRNFLAG( TEXTUREFLAGS_RENDERTARGET )
  89. PRNFLAG( TEXTUREFLAGS_DEPTHRENDERTARGET )
  90. PRNFLAG( TEXTUREFLAGS_NODEBUGOVERRIDE )
  91. PRNFLAG( TEXTUREFLAGS_SINGLECOPY )
  92. PRNFLAG( TEXTUREFLAGS_STAGING_MEMORY )
  93. PRNFLAG( TEXTUREFLAGS_IMMEDIATE_CLEANUP )
  94. PRNFLAG( TEXTUREFLAGS_IGNORE_PICMIP )
  95. PRNFLAG( TEXTUREFLAGS_UNUSED_00400000 )
  96. PRNFLAG( TEXTUREFLAGS_NODEPTHBUFFER )
  97. PRNFLAG( TEXTUREFLAGS_UNUSED_01000000 )
  98. PRNFLAG( TEXTUREFLAGS_CLAMPU )
  99. PRNFLAG( TEXTUREFLAGS_VERTEXTEXTURE )
  100. PRNFLAG( TEXTUREFLAGS_SSBUMP )
  101. PRNFLAG( TEXTUREFLAGS_UNUSED_10000000 )
  102. PRNFLAG( TEXTUREFLAGS_BORDER )
  103. PRNFLAG( TEXTUREFLAGS_STREAMABLE_COARSE )
  104. PRNFLAG( TEXTUREFLAGS_STREAMABLE_FINE )
  105. #undef PRNFLAG
  106. if ( flags )
  107. {
  108. printf( "0x%08X", flags );
  109. }
  110. }
  111. int main( int argc, char **argv )
  112. {
  113. if( argc != 3 )
  114. {
  115. Usage();
  116. return 10;
  117. }
  118. CUtlBuffer file1;
  119. CUtlBuffer file2;
  120. if ( !LoadFileIntoBuffer( argv[1], file1 ) )
  121. return 21;
  122. if ( !LoadFileIntoBuffer( argv[2], file2 ) )
  123. return 22;
  124. IVTFTexture *pTexture1 = CreateVTFTexture();
  125. IVTFTexture *pTexture2 = CreateVTFTexture();
  126. IVTFTexture *arrTextures[2] = { pTexture1, pTexture2 };
  127. bool bMatch = true;
  128. if( !pTexture1->Unserialize( file1 ) )
  129. {
  130. printf( "error loading %s\n", argv[1] );
  131. return 31;
  132. }
  133. if( !pTexture2->Unserialize( file2 ) )
  134. {
  135. printf( "error loading %s\n", argv[2] );
  136. return 32;
  137. }
  138. if( pTexture1->Width() != pTexture2->Width() ||
  139. pTexture1->Height() != pTexture2->Height() ||
  140. pTexture1->Depth() != pTexture2->Depth() )
  141. {
  142. printf( "%s dimensions differ: %dx%dx%d != %dx%dx%d\n",
  143. argv[1],
  144. ( int )pTexture1->Width(), ( int )pTexture1->Height(), ( int )pTexture1->Depth(),
  145. ( int )pTexture2->Width(), ( int )pTexture2->Height(), ( int )pTexture2->Depth() );
  146. bMatch = false;
  147. }
  148. if( pTexture1->LowResWidth() != pTexture2->LowResWidth() ||
  149. pTexture1->LowResHeight() != pTexture2->LowResHeight() )
  150. {
  151. printf( "%s lowres dimensions differ: %dx%d != %dx%d\n",
  152. argv[1],
  153. ( int )pTexture1->LowResWidth(), ( int )pTexture1->LowResHeight(),
  154. ( int )pTexture2->LowResWidth(), ( int )pTexture2->LowResHeight() );
  155. bMatch = false;
  156. }
  157. if( pTexture1->MipCount() != pTexture2->MipCount() )
  158. {
  159. printf( "%s differing mipcounts: %d != %d\n",
  160. argv[1],
  161. ( int )pTexture1->MipCount(), ( int )pTexture2->MipCount() );
  162. bMatch = false;
  163. }
  164. if( pTexture1->FaceCount() != pTexture2->FaceCount() )
  165. {
  166. printf( "%s differing facecount: %d != %d\n",
  167. argv[1],
  168. ( int )pTexture1->FaceCount(), ( int )pTexture2->FaceCount() );
  169. bMatch = false;
  170. }
  171. if( pTexture1->FrameCount() != pTexture2->FrameCount() )
  172. {
  173. printf( "%s differing framecount: %d != %d\n",
  174. argv[1],
  175. ( int )pTexture1->FrameCount(), ( int )pTexture2->FrameCount() );
  176. bMatch = false;
  177. }
  178. if( pTexture1->Flags() != pTexture2->Flags() )
  179. {
  180. printf( "%s differing flags: \"",
  181. argv[1] );
  182. PrintFlags( pTexture1->Flags() );
  183. printf( "\" != \"" );
  184. PrintFlags( pTexture2->Flags() );
  185. printf( "\"\n" );
  186. bMatch = false;
  187. }
  188. if( pTexture1->BumpScale() != pTexture2->BumpScale() )
  189. {
  190. printf( "%s differing bumpscale: %f != %f\n",
  191. argv[1],
  192. ( float )pTexture1->BumpScale(), ( float )pTexture2->BumpScale() );
  193. bMatch = false;
  194. }
  195. if( pTexture1->Format() != pTexture2->Format() )
  196. {
  197. printf( "%s differing image format: %s != %s\n",
  198. argv[1],
  199. ImageLoader::GetName( pTexture1->Format() ),
  200. ImageLoader::GetName( pTexture2->Format() ) );
  201. bMatch = false;
  202. }
  203. if( pTexture1->LowResFormat() != pTexture2->LowResFormat() )
  204. {
  205. Assert(0);
  206. printf( "%s differing lowres image format: %s != %s\n",
  207. argv[1],
  208. ImageLoader::GetName( pTexture1->LowResFormat() ),
  209. ImageLoader::GetName( pTexture2->LowResFormat() ) );
  210. bMatch = false;
  211. }
  212. const Vector &vReflectivity1 = pTexture1->Reflectivity();
  213. const Vector &vReflectivity2 = pTexture2->Reflectivity();
  214. if( !VectorsAreEqual( vReflectivity1, vReflectivity2, 0.0001f ) )
  215. {
  216. printf( "%s differing reflectivity: [%f,%f,%f] != [%f,%f,%f]\n",
  217. argv[1],
  218. ( float )pTexture1->Reflectivity()[0],
  219. ( float )pTexture1->Reflectivity()[1],
  220. ( float )pTexture1->Reflectivity()[2],
  221. ( float )pTexture2->Reflectivity()[0],
  222. ( float )pTexture2->Reflectivity()[1],
  223. ( float )pTexture2->Reflectivity()[2] );
  224. bMatch = false;
  225. }
  226. if ( pTexture1->ComputeTotalSize() != pTexture2->ComputeTotalSize() )
  227. {
  228. printf( "%s differing image data size: %d != %d\n",
  229. argv[1],
  230. ( int )pTexture1->ComputeTotalSize(), ( int )pTexture2->ComputeTotalSize() );
  231. bMatch = false;
  232. }
  233. if ( bMatch )
  234. {
  235. unsigned char const *pData1 = pTexture1->ImageData();
  236. unsigned char const *pData2 = pTexture2->ImageData();
  237. int const iSize = pTexture1->ComputeTotalSize();
  238. if( memcmp( pData1, pData2, iSize) != 0 )
  239. {
  240. printf( "%s image data different\n", argv[1] );
  241. if (( pTexture1->Format() == IMAGE_FORMAT_DXT1 ) || ( pTexture1->Format() == IMAGE_FORMAT_DXT3 ) ||
  242. ( pTexture1->Format() == IMAGE_FORMAT_DXT5 ) || ( pTexture1->Format() == IMAGE_FORMAT_ATI2N ) ||
  243. ( pTexture1->Format() == IMAGE_FORMAT_ATI1N ) )
  244. {
  245. int i, numOffsetsComplained = 0;
  246. for( i = 0; i < iSize; i++ )
  247. {
  248. if( pData1[i] != pData2[i] )
  249. {
  250. printf( "image data at offset %d different\n", i );
  251. if ( numOffsetsComplained ++ > 10 )
  252. {
  253. printf( "image data significantly differs!\n" );
  254. break;
  255. }
  256. }
  257. }
  258. }
  259. else
  260. {
  261. for( int iFrame = 0; iFrame < pTexture1->FrameCount(); ++iFrame )
  262. {
  263. for ( int iMipLevel = 0; iMipLevel < pTexture1->MipCount(); ++iMipLevel )
  264. {
  265. int nMipWidth, nMipHeight, nMipDepth;
  266. pTexture1->ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
  267. for (int iCubeFace = 0; iCubeFace < pTexture1->FrameCount(); ++iCubeFace)
  268. {
  269. for ( int z = 0; z < nMipDepth; ++z )
  270. {
  271. pData1 = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z );
  272. pData2 = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z );
  273. int nMipSize = pTexture1->ComputeMipSize( iMipLevel );
  274. if ( memcmp( pData1, pData2, nMipSize ) )
  275. {
  276. bool bBreak = false;
  277. for ( int y = 0; y < nMipHeight; ++y )
  278. {
  279. for ( int x = 0; x < nMipWidth; ++x )
  280. {
  281. unsigned char const *pData1a = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z );
  282. unsigned char const *pData2a = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z );
  283. if ( memcmp( pData1a, pData2a, ImageLoader::SizeInBytes( pTexture1->Format() ) ) )
  284. {
  285. printf( "Frame %d Mip level %d Face %d Z-slice %d texel (%d,%d) different!\n",
  286. iFrame, iMipLevel, iCubeFace, z, x, y );
  287. bBreak = true;
  288. break;
  289. }
  290. }
  291. if ( bBreak )
  292. break;
  293. }
  294. }
  295. }
  296. }
  297. }
  298. }
  299. }
  300. bMatch = false;
  301. }
  302. }
  303. // Lowres data
  304. {
  305. int iDummy, iSize1, iSize2;
  306. pTexture1->LowResFileInfo( &iDummy, &iSize1 );
  307. pTexture2->LowResFileInfo( &iDummy, &iSize2 );
  308. if ( iSize1 != iSize2 )
  309. {
  310. printf( "%s differing low res image data size: %d != %d\n", argv[1], iSize1, iSize2 );
  311. bMatch = false;
  312. }
  313. if ( bMatch )
  314. {
  315. if ( memcmp( pTexture1->LowResImageData(), pTexture2->LowResImageData(), iSize1 ) != 0 )
  316. {
  317. printf( "%s differing low res image data\n",
  318. argv[1] );
  319. bMatch = false;
  320. }
  321. }
  322. }
  323. // Check other resources
  324. {
  325. int numRes1 = pTexture1->GetResourceTypes( NULL, 0 );
  326. int numRes2 = pTexture2->GetResourceTypes( NULL, 0 );
  327. // List of resource types checked or insignificant diffs
  328. typedef CUtlMap< int, bool > MapResTypes;
  329. MapResTypes mapTypes( DefLessFunc( int ) );
  330. mapTypes.Insert( VTF_LEGACY_RSRC_LOW_RES_IMAGE, true );
  331. mapTypes.Insert( VTF_LEGACY_RSRC_IMAGE, true );
  332. mapTypes.Insert( MK_VTF_RSRC_ID( 'C','R','C' ), true );
  333. uint32 *puiresbuffer = ( uint32 * ) stackalloc( ( numRes1 + numRes2 ) * sizeof( uint32 ) );
  334. int arrNums[2] = { numRes1, numRes2 };
  335. for ( int itx = 0; itx < 2; ++ itx )
  336. {
  337. arrTextures[itx]->GetResourceTypes( puiresbuffer, arrNums[itx] );
  338. while ( arrNums[itx] --> 0 )
  339. {
  340. uint32 uiResType = puiresbuffer[ arrNums[itx] ];
  341. if ( mapTypes.Find( uiResType ) != mapTypes.InvalidIndex() )
  342. continue;
  343. mapTypes.Insert( uiResType, true );
  344. size_t numBytes1, numBytes2;
  345. void const *pvResData1 = pTexture1->GetResourceData( uiResType, &numBytes1 );
  346. void const *pvResData2 = pTexture2->GetResourceData( uiResType, &numBytes2 );
  347. if ( !pvResData1 != !pvResData2 )
  348. {
  349. printf( "%s different resource %s %s\n",
  350. argv[1],
  351. ResourceToString( uiResType ),
  352. pvResData1 ? "present" : "missing" );
  353. bMatch = false;
  354. }
  355. else if ( numBytes1 != numBytes2 )
  356. {
  357. printf( "%s different resource %s size %lld != %lld\n",
  358. argv[1],
  359. ResourceToString( uiResType ),
  360. (long long)numBytes1, (long long)numBytes2 );
  361. bMatch = false;
  362. }
  363. else if ( memcmp( pvResData1, pvResData2, numBytes1 ) != 0 )
  364. {
  365. printf( "%s different resource %s data\n",
  366. argv[1],
  367. ResourceToString( uiResType ) );
  368. bMatch = false;
  369. }
  370. }
  371. }
  372. }
  373. if( bMatch )
  374. {
  375. return 0;
  376. }
  377. else
  378. {
  379. return 1;
  380. }
  381. }