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
438 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include "vtf/vtf.h" |
|
#include "tier1/UtlBuffer.h" |
|
#include "tier1/utlmap.h" |
|
#include "bitmap/imageformat.h" |
|
#include "mathlib/vector.h" |
|
#include <conio.h> |
|
|
|
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; |
|
} |
|
}
|
|
|