//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include #include #include #include #include "vtf/vtf.h" #include "tier1/UtlBuffer.h" #include "tier1/utlmap.h" #include "bitmap/imageformat.h" #include "mathlib/vector.h" #include void Usage( void ) { printf( "Usage: vtfdiff file1.vtf file2.vtf\n" ); } bool LoadFileIntoBuffer( const char *pFileName, CUtlBuffer &buf ) { struct _stat statBuf; if( _stat( pFileName, &statBuf ) != 0 ) { goto error; } buf.EnsureCapacity( statBuf.st_size ); FILE *fp; fp = fopen( pFileName, "rb" ); if( !fp ) { goto error; } int nBytesRead = fread( buf.Base(), 1, statBuf.st_size, fp ); fclose( fp ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead ); return true; error: printf( "Can't find file %s\n", pFileName ); return false; } char const * ResourceToString( uint32 uiResType ) { static char chBuffer[256]; switch ( uiResType ) { case VTF_LEGACY_RSRC_LOW_RES_IMAGE: return "VTF_LEGACY_RSRC_LOW_RES_IMAGE"; case VTF_LEGACY_RSRC_IMAGE: return "VTF_LEGACY_RSRC_IMAGE"; case VTF_RSRC_SHEET: return "VTF_RSRC_SHEET"; case MK_VTF_RSRC_ID( 'C','R','C' ): return "CRC"; case VTF_RSRC_TEXTURE_LOD_SETTINGS: return "VTF_RSRC_TEXTURE_LOD_SETTINGS"; default: sprintf( chBuffer, "0x%08X", uiResType ); return chBuffer; } return chBuffer; } void PrintFlags( int flags ) { #define PRNFLAG( flagname ) \ if ( ( flags & (flagname) ) == (flagname) ) \ { \ flags &=~ (flagname); \ printf( "%s%s", #flagname + strlen("TEXTUREFLAGS_"), flags ? "|" : "" ); \ } \ PRNFLAG( TEXTUREFLAGS_POINTSAMPLE ) PRNFLAG( TEXTUREFLAGS_TRILINEAR ) PRNFLAG( TEXTUREFLAGS_CLAMPS ) PRNFLAG( TEXTUREFLAGS_CLAMPT ) PRNFLAG( TEXTUREFLAGS_ANISOTROPIC ) PRNFLAG( TEXTUREFLAGS_HINT_DXT5 ) PRNFLAG( TEXTUREFLAGS_SRGB ) PRNFLAG( TEXTUREFLAGS_NORMAL ) PRNFLAG( TEXTUREFLAGS_NOMIP ) PRNFLAG( TEXTUREFLAGS_NOLOD ) PRNFLAG( TEXTUREFLAGS_ALL_MIPS ) PRNFLAG( TEXTUREFLAGS_PROCEDURAL ) PRNFLAG( TEXTUREFLAGS_ONEBITALPHA ) PRNFLAG( TEXTUREFLAGS_EIGHTBITALPHA ) PRNFLAG( TEXTUREFLAGS_ENVMAP ) PRNFLAG( TEXTUREFLAGS_RENDERTARGET ) PRNFLAG( TEXTUREFLAGS_DEPTHRENDERTARGET ) PRNFLAG( TEXTUREFLAGS_NODEBUGOVERRIDE ) PRNFLAG( TEXTUREFLAGS_SINGLECOPY ) PRNFLAG( TEXTUREFLAGS_STAGING_MEMORY ) PRNFLAG( TEXTUREFLAGS_IMMEDIATE_CLEANUP ) PRNFLAG( TEXTUREFLAGS_IGNORE_PICMIP ) PRNFLAG( TEXTUREFLAGS_UNUSED_00400000 ) PRNFLAG( TEXTUREFLAGS_NODEPTHBUFFER ) PRNFLAG( TEXTUREFLAGS_UNUSED_01000000 ) PRNFLAG( TEXTUREFLAGS_CLAMPU ) PRNFLAG( TEXTUREFLAGS_VERTEXTEXTURE ) PRNFLAG( TEXTUREFLAGS_SSBUMP ) PRNFLAG( TEXTUREFLAGS_UNUSED_10000000 ) PRNFLAG( TEXTUREFLAGS_BORDER ) PRNFLAG( TEXTUREFLAGS_STREAMABLE_COARSE ) PRNFLAG( TEXTUREFLAGS_STREAMABLE_FINE ) #undef PRNFLAG if ( flags ) { printf( "0x%08X", flags ); } } int main( int argc, char **argv ) { if( argc != 3 ) { Usage(); return 10; } CUtlBuffer file1; CUtlBuffer file2; if ( !LoadFileIntoBuffer( argv[1], file1 ) ) return 21; if ( !LoadFileIntoBuffer( argv[2], file2 ) ) return 22; IVTFTexture *pTexture1 = CreateVTFTexture(); IVTFTexture *pTexture2 = CreateVTFTexture(); IVTFTexture *arrTextures[2] = { pTexture1, pTexture2 }; bool bMatch = true; if( !pTexture1->Unserialize( file1 ) ) { printf( "error loading %s\n", argv[1] ); return 31; } if( !pTexture2->Unserialize( file2 ) ) { printf( "error loading %s\n", argv[2] ); return 32; } if( pTexture1->Width() != pTexture2->Width() || pTexture1->Height() != pTexture2->Height() || pTexture1->Depth() != pTexture2->Depth() ) { printf( "%s dimensions differ: %dx%dx%d != %dx%dx%d\n", argv[1], ( int )pTexture1->Width(), ( int )pTexture1->Height(), ( int )pTexture1->Depth(), ( int )pTexture2->Width(), ( int )pTexture2->Height(), ( int )pTexture2->Depth() ); bMatch = false; } if( pTexture1->LowResWidth() != pTexture2->LowResWidth() || pTexture1->LowResHeight() != pTexture2->LowResHeight() ) { printf( "%s lowres dimensions differ: %dx%d != %dx%d\n", argv[1], ( int )pTexture1->LowResWidth(), ( int )pTexture1->LowResHeight(), ( int )pTexture2->LowResWidth(), ( int )pTexture2->LowResHeight() ); bMatch = false; } if( pTexture1->MipCount() != pTexture2->MipCount() ) { printf( "%s differing mipcounts: %d != %d\n", argv[1], ( int )pTexture1->MipCount(), ( int )pTexture2->MipCount() ); bMatch = false; } if( pTexture1->FaceCount() != pTexture2->FaceCount() ) { printf( "%s differing facecount: %d != %d\n", argv[1], ( int )pTexture1->FaceCount(), ( int )pTexture2->FaceCount() ); bMatch = false; } if( pTexture1->FrameCount() != pTexture2->FrameCount() ) { printf( "%s differing framecount: %d != %d\n", argv[1], ( int )pTexture1->FrameCount(), ( int )pTexture2->FrameCount() ); bMatch = false; } if( pTexture1->Flags() != pTexture2->Flags() ) { printf( "%s differing flags: \"", argv[1] ); PrintFlags( pTexture1->Flags() ); printf( "\" != \"" ); PrintFlags( pTexture2->Flags() ); printf( "\"\n" ); bMatch = false; } if( pTexture1->BumpScale() != pTexture2->BumpScale() ) { printf( "%s differing bumpscale: %f != %f\n", argv[1], ( float )pTexture1->BumpScale(), ( float )pTexture2->BumpScale() ); bMatch = false; } if( pTexture1->Format() != pTexture2->Format() ) { printf( "%s differing image format: %s != %s\n", argv[1], ImageLoader::GetName( pTexture1->Format() ), ImageLoader::GetName( pTexture2->Format() ) ); bMatch = false; } if( pTexture1->LowResFormat() != pTexture2->LowResFormat() ) { Assert(0); printf( "%s differing lowres image format: %s != %s\n", argv[1], ImageLoader::GetName( pTexture1->LowResFormat() ), ImageLoader::GetName( pTexture2->LowResFormat() ) ); bMatch = false; } const Vector &vReflectivity1 = pTexture1->Reflectivity(); const Vector &vReflectivity2 = pTexture2->Reflectivity(); if( !VectorsAreEqual( vReflectivity1, vReflectivity2, 0.0001f ) ) { printf( "%s differing reflectivity: [%f,%f,%f] != [%f,%f,%f]\n", argv[1], ( float )pTexture1->Reflectivity()[0], ( float )pTexture1->Reflectivity()[1], ( float )pTexture1->Reflectivity()[2], ( float )pTexture2->Reflectivity()[0], ( float )pTexture2->Reflectivity()[1], ( float )pTexture2->Reflectivity()[2] ); bMatch = false; } if ( pTexture1->ComputeTotalSize() != pTexture2->ComputeTotalSize() ) { printf( "%s differing image data size: %d != %d\n", argv[1], ( int )pTexture1->ComputeTotalSize(), ( int )pTexture2->ComputeTotalSize() ); bMatch = false; } if ( bMatch ) { unsigned char const *pData1 = pTexture1->ImageData(); unsigned char const *pData2 = pTexture2->ImageData(); int const iSize = pTexture1->ComputeTotalSize(); if( memcmp( pData1, pData2, iSize) != 0 ) { printf( "%s image data different\n", argv[1] ); if (( pTexture1->Format() == IMAGE_FORMAT_DXT1 ) || ( pTexture1->Format() == IMAGE_FORMAT_DXT3 ) || ( pTexture1->Format() == IMAGE_FORMAT_DXT5 ) || ( pTexture1->Format() == IMAGE_FORMAT_ATI2N ) || ( pTexture1->Format() == IMAGE_FORMAT_ATI1N ) ) { int i, numOffsetsComplained = 0; for( i = 0; i < iSize; i++ ) { if( pData1[i] != pData2[i] ) { printf( "image data at offset %d different\n", i ); if ( numOffsetsComplained ++ > 10 ) { printf( "image data significantly differs!\n" ); break; } } } } else { for( int iFrame = 0; iFrame < pTexture1->FrameCount(); ++iFrame ) { for ( int iMipLevel = 0; iMipLevel < pTexture1->MipCount(); ++iMipLevel ) { int nMipWidth, nMipHeight, nMipDepth; pTexture1->ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth ); for (int iCubeFace = 0; iCubeFace < pTexture1->FrameCount(); ++iCubeFace) { for ( int z = 0; z < nMipDepth; ++z ) { pData1 = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z ); pData2 = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z ); int nMipSize = pTexture1->ComputeMipSize( iMipLevel ); if ( memcmp( pData1, pData2, nMipSize ) ) { bool bBreak = false; for ( int y = 0; y < nMipHeight; ++y ) { for ( int x = 0; x < nMipWidth; ++x ) { unsigned char const *pData1a = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z ); unsigned char const *pData2a = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z ); if ( memcmp( pData1a, pData2a, ImageLoader::SizeInBytes( pTexture1->Format() ) ) ) { printf( "Frame %d Mip level %d Face %d Z-slice %d texel (%d,%d) different!\n", iFrame, iMipLevel, iCubeFace, z, x, y ); bBreak = true; break; } } if ( bBreak ) break; } } } } } } } bMatch = false; } } // Lowres data { int iDummy, iSize1, iSize2; pTexture1->LowResFileInfo( &iDummy, &iSize1 ); pTexture2->LowResFileInfo( &iDummy, &iSize2 ); if ( iSize1 != iSize2 ) { printf( "%s differing low res image data size: %d != %d\n", argv[1], iSize1, iSize2 ); bMatch = false; } if ( bMatch ) { if ( memcmp( pTexture1->LowResImageData(), pTexture2->LowResImageData(), iSize1 ) != 0 ) { printf( "%s differing low res image data\n", argv[1] ); bMatch = false; } } } // Check other resources { int numRes1 = pTexture1->GetResourceTypes( NULL, 0 ); int numRes2 = pTexture2->GetResourceTypes( NULL, 0 ); // List of resource types checked or insignificant diffs typedef CUtlMap< int, bool > MapResTypes; MapResTypes mapTypes( DefLessFunc( int ) ); mapTypes.Insert( VTF_LEGACY_RSRC_LOW_RES_IMAGE, true ); mapTypes.Insert( VTF_LEGACY_RSRC_IMAGE, true ); mapTypes.Insert( MK_VTF_RSRC_ID( 'C','R','C' ), true ); uint32 *puiresbuffer = ( uint32 * ) stackalloc( ( numRes1 + numRes2 ) * sizeof( uint32 ) ); int arrNums[2] = { numRes1, numRes2 }; for ( int itx = 0; itx < 2; ++ itx ) { arrTextures[itx]->GetResourceTypes( puiresbuffer, arrNums[itx] ); while ( arrNums[itx] --> 0 ) { uint32 uiResType = puiresbuffer[ arrNums[itx] ]; if ( mapTypes.Find( uiResType ) != mapTypes.InvalidIndex() ) continue; mapTypes.Insert( uiResType, true ); size_t numBytes1, numBytes2; void const *pvResData1 = pTexture1->GetResourceData( uiResType, &numBytes1 ); void const *pvResData2 = pTexture2->GetResourceData( uiResType, &numBytes2 ); if ( !pvResData1 != !pvResData2 ) { printf( "%s different resource %s %s\n", argv[1], ResourceToString( uiResType ), pvResData1 ? "present" : "missing" ); bMatch = false; } else if ( numBytes1 != numBytes2 ) { printf( "%s different resource %s size %lld != %lld\n", argv[1], ResourceToString( uiResType ), (long long)numBytes1, (long long)numBytes2 ); bMatch = false; } else if ( memcmp( pvResData1, pvResData2, numBytes1 ) != 0 ) { printf( "%s different resource %s data\n", argv[1], ResourceToString( uiResType ) ); bMatch = false; } } } } if( bMatch ) { return 0; } else { return 1; } }