summaryrefslogtreecommitdiff
path: root/vtf/convert_x360.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vtf/convert_x360.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'vtf/convert_x360.cpp')
-rw-r--r--vtf/convert_x360.cpp891
1 files changed, 891 insertions, 0 deletions
diff --git a/vtf/convert_x360.cpp b/vtf/convert_x360.cpp
new file mode 100644
index 0000000..b0e0ee3
--- /dev/null
+++ b/vtf/convert_x360.cpp
@@ -0,0 +1,891 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//
+// Purpose: Force pc .VTF to preferred .VTF 360 format conversion
+//
+//=====================================================================================//
+
+#include "tier1/utlvector.h"
+#include "mathlib/mathlib.h"
+#include "tier1/strtools.h"
+#include "cvtf.h"
+#include "tier1/utlbuffer.h"
+#include "tier0/dbg.h"
+#include "tier1/utlmemory.h"
+#include "bitmap/imageformat.h"
+
+// if the entire vtf file is smaller than this threshold, add entirely to preload
+#define PRELOAD_VTF_THRESHOLD 2048
+
+struct ResourceCopy_t
+{
+ void *m_pData;
+ int m_DataLength;
+ ResourceEntryInfo m_EntryInfo;
+};
+
+//-----------------------------------------------------------------------------
+// Converts to an alternate format
+//-----------------------------------------------------------------------------
+ImageFormat PreferredFormat( IVTFTexture *pVTFTexture, ImageFormat fmt, int width, int height, int mipCount, int faceCount )
+{
+ switch ( fmt )
+ {
+ case IMAGE_FORMAT_RGBA8888:
+ case IMAGE_FORMAT_ABGR8888:
+ case IMAGE_FORMAT_ARGB8888:
+ case IMAGE_FORMAT_BGRA8888:
+ return IMAGE_FORMAT_BGRA8888;
+
+ // 24bpp gpu formats don't exist, must convert
+ case IMAGE_FORMAT_BGRX8888:
+ case IMAGE_FORMAT_RGB888:
+ case IMAGE_FORMAT_BGR888:
+ case IMAGE_FORMAT_RGB888_BLUESCREEN:
+ case IMAGE_FORMAT_BGR888_BLUESCREEN:
+ return IMAGE_FORMAT_BGRX8888;
+
+ case IMAGE_FORMAT_BGRX5551:
+ case IMAGE_FORMAT_RGB565:
+ case IMAGE_FORMAT_BGR565:
+ return IMAGE_FORMAT_BGR565;
+
+ // no change
+ case IMAGE_FORMAT_I8:
+ case IMAGE_FORMAT_IA88:
+ case IMAGE_FORMAT_A8:
+ case IMAGE_FORMAT_BGRA4444:
+ case IMAGE_FORMAT_BGRA5551:
+ case IMAGE_FORMAT_UV88:
+ case IMAGE_FORMAT_UVWQ8888:
+ case IMAGE_FORMAT_RGBA16161616:
+ case IMAGE_FORMAT_UVLX8888:
+ case IMAGE_FORMAT_DXT1_ONEBITALPHA:
+ case IMAGE_FORMAT_DXT1:
+ case IMAGE_FORMAT_DXT3:
+ case IMAGE_FORMAT_DXT5:
+ case IMAGE_FORMAT_ATI1N:
+ case IMAGE_FORMAT_ATI2N:
+ break;
+
+ case IMAGE_FORMAT_RGBA16161616F:
+ return IMAGE_FORMAT_RGBA16161616;
+ }
+
+ return fmt;
+}
+
+//-----------------------------------------------------------------------------
+// Determines target dimensions
+//-----------------------------------------------------------------------------
+bool ComputeTargetDimensions( const char *pDebugName, IVTFTexture *pVTFTexture, int picmip, int &width, int &height, int &mipCount, int &mipSkipCount, bool &bNoMip )
+{
+ width = pVTFTexture->Width();
+ height = pVTFTexture->Height();
+
+ // adhere to texture's internal lod setting
+ int nClampX = 1<<30;
+ int nClampY = 1<<30;
+ TextureLODControlSettings_t const *pLODInfo = reinterpret_cast<TextureLODControlSettings_t const *> ( pVTFTexture->GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) );
+ if ( pLODInfo )
+ {
+ if ( pLODInfo->m_ResolutionClampX )
+ {
+ nClampX = min( nClampX, 1 << pLODInfo->m_ResolutionClampX );
+ }
+ if ( pLODInfo->m_ResolutionClampX_360 )
+ {
+ nClampX = min( nClampX, 1 << pLODInfo->m_ResolutionClampX_360 );
+ }
+ if ( pLODInfo->m_ResolutionClampY )
+ {
+ nClampY = min( nClampY, 1 << pLODInfo->m_ResolutionClampY );
+ }
+ if ( pLODInfo->m_ResolutionClampY_360 )
+ {
+ nClampY = min( nClampY, 1 << pLODInfo->m_ResolutionClampY_360 );
+ }
+ }
+
+ // spin down to desired texture size
+ mipSkipCount = 0;
+ while ( mipSkipCount < picmip || width > nClampX || height > nClampY )
+ {
+ if ( width == 1 && height == 1 )
+ break;
+ width >>= 1;
+ height >>= 1;
+ if ( width < 1 )
+ width = 1;
+ if ( height < 1 )
+ height = 1;
+ mipSkipCount++;
+ }
+
+ bNoMip = false;
+ if ( pVTFTexture->Flags() & TEXTUREFLAGS_NOMIP )
+ {
+ bNoMip = true;
+ }
+
+ // determine mip quantity based on desired width/height
+ if ( bNoMip )
+ {
+ // avoid serializing unused mips
+ mipCount = 1;
+ }
+ else
+ {
+ mipCount = ImageLoader::GetNumMipMapLevels( width, height );
+ }
+
+ // success
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Align the buffer to specified boundary
+//-----------------------------------------------------------------------------
+int AlignBuffer( CUtlBuffer &buf, int alignment )
+{
+ int curPosition;
+ int newPosition;
+ byte padByte = 0;
+
+ // advance to aligned position
+ buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
+ curPosition = buf.TellPut();
+ newPosition = AlignValue( curPosition, alignment );
+ buf.EnsureCapacity( newPosition );
+
+ // write empty
+ for ( int i=0; i<newPosition-curPosition; i++ )
+ {
+ buf.Put( &padByte, 1 );
+ }
+
+ return newPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the x86 image data to 360
+//-----------------------------------------------------------------------------
+bool ConvertImageFormatEx(
+ unsigned char *pSourceImage,
+ int sourceImageSize,
+ ImageFormat sourceFormat,
+ unsigned char *pTargetImage,
+ int targetImageSize,
+ ImageFormat targetFormat,
+ int width,
+ int height,
+ bool bSrgbGammaConvert )
+{
+ // format conversion expects pc oriented data
+ // but, formats that are >8 bits per channels need to be element pre-swapped
+ ImageLoader::PreConvertSwapImageData( pSourceImage, sourceImageSize, sourceFormat );
+
+ bool bRetVal = ImageLoader::ConvertImageFormat(
+ pSourceImage,
+ sourceFormat,
+ pTargetImage,
+ targetFormat,
+ width,
+ height );
+ if ( !bRetVal )
+ {
+ return false;
+ }
+
+ // convert to proper channel order for 360 d3dformats
+ ImageLoader::PostConvertSwapImageData( pTargetImage, targetImageSize, targetFormat );
+
+ // Convert colors from sRGB gamma space into 360 piecewise linear gamma space
+ if ( bSrgbGammaConvert == true )
+ {
+ if ( targetFormat == IMAGE_FORMAT_BGRA8888 || targetFormat == IMAGE_FORMAT_BGRX8888 )
+ {
+ //Msg( " Converting 8888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
+ for ( int i = 0; i < ( targetImageSize / 4 ); i++ ) // targetImageSize is the raw data length in bytes
+ {
+ unsigned char *pRGB[3] = { NULL, NULL, NULL };
+
+ if ( IsPC() )
+ {
+ // pTargetImage is the raw image data
+ pRGB[0] = &( pTargetImage[ ( i * 4 ) + 1 ] ); // Red
+ pRGB[1] = &( pTargetImage[ ( i * 4 ) + 2 ] ); // Green
+ pRGB[2] = &( pTargetImage[ ( i * 4 ) + 3 ] ); // Blue
+ }
+ else // 360
+ {
+ // pTargetImage is the raw image data
+ pRGB[0] = &( pTargetImage[ ( i * 4 ) + 1 ] ); // Red
+ pRGB[1] = &( pTargetImage[ ( i * 4 ) + 2 ] ); // Green
+ pRGB[2] = &( pTargetImage[ ( i * 4 ) + 3 ] ); // Blue
+ }
+
+ // Modify RGB data in place
+ for ( int j = 0; j < 3; j++ ) // For red, green, blue
+ {
+ float flSrgbGamma = float( *( pRGB[j] ) ) / 255.0f;
+ float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
+
+ fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
+ *( pRGB[j] ) = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
+ }
+ }
+ }
+ else if ( ( targetFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA ) || ( targetFormat == IMAGE_FORMAT_DXT1 ) || ( targetFormat == IMAGE_FORMAT_DXT3 ) || ( targetFormat == IMAGE_FORMAT_DXT5 ) )
+ {
+ //Msg( " Converting DXT texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
+ int nStrideBytes = 8;
+ int nOffsetBytes = 0;
+ if ( ( targetFormat == IMAGE_FORMAT_DXT3 ) || ( targetFormat == IMAGE_FORMAT_DXT5 ) )
+ {
+ nOffsetBytes = 8;
+ nStrideBytes = 16;
+ }
+
+ for ( int i = 0; i < ( targetImageSize / nStrideBytes ); i++ ) // For each color or color/alpha block
+ {
+ // Get 16bit 565 colors into an unsigned short
+ unsigned short n565Color0 = 0;
+ n565Color0 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 0 ] ) ) << 8;
+ n565Color0 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 1 ] ) );
+
+ unsigned short n565Color1 = 0;
+ n565Color1 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 2 ] ) ) << 8;
+ n565Color1 |= ( ( unsigned short )( unsigned char )( pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 3 ] ) );
+
+ // Convert to 888
+ unsigned char v888Color0[3];
+ v888Color0[0] = ( ( ( n565Color0 >> 11 ) & 0x1f ) << 3 );
+ v888Color0[1] = ( ( ( n565Color0 >> 5 ) & 0x3f ) << 2 );
+ v888Color0[2] = ( ( n565Color0 & 0x1f ) << 3 );
+
+ // Since we have one bit less of red and blue, add some of the error back in
+ if ( v888Color0[0] != 0 ) // Don't mess with black pixels
+ v888Color0[0] |= 0x04; // Add 0.5 of the error
+ if ( v888Color0[2] != 0 ) // Don't mess with black pixels
+ v888Color0[2] |= 0x04; // Add 0.5 of the error
+
+ unsigned char v888Color1[3];
+ v888Color1[0] = ( ( ( n565Color1 >> 11 ) & 0x1f ) << 3 );
+ v888Color1[1] = ( ( ( n565Color1 >> 5 ) & 0x3f ) << 2 );
+ v888Color1[2] = ( ( n565Color1 & 0x1f ) << 3 );
+
+ // Since we have one bit less of red and blue, add some of the error back in
+ if ( v888Color1[0] != 0 ) // Don't mess with black pixels
+ v888Color1[0] |= 0x04; // Add 0.5 of the error
+ if ( v888Color1[2] != 0 ) // Don't mess with black pixels
+ v888Color1[2] |= 0x04; // Add 0.5 of the error
+
+ // Convert to float
+ float vFlColor0[3];
+ vFlColor0[0] = float( v888Color0[0] ) / 255.0f;
+ vFlColor0[1] = float( v888Color0[1] ) / 255.0f;
+ vFlColor0[2] = float( v888Color0[2] ) / 255.0f;
+
+ float vFlColor1[3];
+ vFlColor1[0] = float( v888Color1[0] ) / 255.0f;
+ vFlColor1[1] = float( v888Color1[1] ) / 255.0f;
+ vFlColor1[2] = float( v888Color1[2] ) / 255.0f;
+
+ // Modify float RGB data and write to output 888 colors
+ unsigned char v888Color0New[3];
+ unsigned char v888Color1New[3];
+ for ( int j = 0; j < 3; j++ ) // For red, green, blue
+ {
+ for ( int k = 0; k < 2; k++ ) // For color0 and color1
+ {
+ float *pFlValue = ( k == 0 ) ? &( vFlColor0[j] ) : &( vFlColor1[j] );
+ unsigned char *p8BitValue = ( k == 0 ) ? &( v888Color0New[j] ) : &( v888Color1New[j] );
+
+ float flSrgbGamma = *pFlValue;
+ float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
+
+ fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
+ //*p8BitValue = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
+ *p8BitValue = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) ), 0.0f, 255.0f ) );
+ }
+ }
+
+ // Convert back to 565
+ v888Color0New[0] &= 0xf8; // 5 bits
+ v888Color0New[1] &= 0xfc; // 6 bits
+ v888Color0New[2] &= 0xf8; // 5 bits
+ unsigned short n565Color0New = ( ( unsigned short )v888Color0New[0] << 8 ) | ( ( unsigned short )v888Color0New[1] << 3 ) | ( ( unsigned short )v888Color0New[2] >> 3 );
+
+ v888Color1New[0] &= 0xf8; // 5 bits
+ v888Color1New[1] &= 0xfc; // 6 bits
+ v888Color1New[2] &= 0xf8; // 5 bits
+ unsigned short n565Color1New = ( ( unsigned short )v888Color1New[0] << 8 ) | ( ( unsigned short )v888Color1New[1] << 3 ) | ( ( unsigned short )v888Color1New[2] >> 3 );
+
+ // If we're targeting DXT1, make sure we haven't made a non transparent color block transparent
+ if ( ( targetFormat == IMAGE_FORMAT_DXT1 ) || ( targetFormat == IMAGE_FORMAT_DXT1_ONEBITALPHA ) )
+ {
+ // If new block is transparent but old block wasn't
+ if ( ( n565Color0New <= n565Color1New ) && ( n565Color0 > n565Color1 ) )
+ {
+ if ( ( v888Color0New[0] == v888Color1New[0] ) && ( v888Color0[0] != v888Color1[0] ) )
+ {
+ if ( v888Color0New[0] == 0xf8 )
+ v888Color1New[0] -= 0x08;
+ else
+ v888Color0New[0] += 0x08;
+ }
+
+ if ( ( v888Color0New[1] == v888Color1New[1] ) && ( v888Color0[1] != v888Color1[1] ) )
+ {
+ if ( v888Color0New[1] == 0xfc )
+ v888Color1New[1] -= 0x04;
+ else
+ v888Color0New[1] += 0x04;
+ }
+
+ if ( ( v888Color0New[2] == v888Color1New[2] ) && ( v888Color0[2] != v888Color1[2] ) )
+ {
+ if ( v888Color0New[2] == 0xf8 )
+ v888Color1New[2] -= 0x08;
+ else
+ v888Color0New[2] += 0x08;
+ }
+
+ n565Color0New = ( ( unsigned short )v888Color0New[0] << 8 ) | ( ( unsigned short )v888Color0New[1] << 3 ) | ( ( unsigned short )v888Color0New[2] >> 3 );
+ n565Color1New = ( ( unsigned short )v888Color1New[0] << 8 ) | ( ( unsigned short )v888Color1New[1] << 3 ) | ( ( unsigned short )v888Color1New[2] >> 3 );
+ }
+ }
+
+ // Copy new colors back to color block
+ pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 0 ] = ( unsigned char )( ( n565Color0New >> 8 ) & 0x00ff );
+ pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 1 ] = ( unsigned char )( n565Color0New & 0x00ff );
+
+ pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 2 ] = ( unsigned char )( ( n565Color1New >> 8 ) & 0x00ff );
+ pTargetImage[ ( i * nStrideBytes ) + nOffsetBytes + 3 ] = ( unsigned char )( n565Color1New & 0x00ff );
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Write the source data as the desired format into a target buffer
+//-----------------------------------------------------------------------------
+bool SerializeImageData( IVTFTexture *pSourceVTF, int frame, int face, int mip, ImageFormat targetFormat, CUtlBuffer &targetBuf )
+{
+ int width;
+ int height;
+ int targetImageSize;
+ byte *pSourceImage;
+ int sourceImageSize;
+ int targetSize;
+ CUtlMemory<byte> targetImage;
+
+ width = pSourceVTF->Width() >> mip;
+ height = pSourceVTF->Height() >> mip;
+ if ( width < 1 )
+ width = 1;
+ if ( height < 1)
+ height = 1;
+
+ sourceImageSize = ImageLoader::GetMemRequired( width, height, 1, pSourceVTF->Format(), false );
+ pSourceImage = pSourceVTF->ImageData( frame, face, mip );
+
+ targetImageSize = ImageLoader::GetMemRequired( width, height, 1, targetFormat, false );
+ targetImage.EnsureCapacity( targetImageSize );
+ byte *pTargetImage = (byte*)targetImage.Base();
+
+ // conversion may skip bytes, ensure all bits initialized
+ memset( pTargetImage, 0xFF, targetImageSize );
+
+ // format conversion expects pc oriented data
+ bool bRetVal = ConvertImageFormatEx(
+ pSourceImage,
+ sourceImageSize,
+ pSourceVTF->Format(),
+ pTargetImage,
+ targetImageSize,
+ targetFormat,
+ width,
+ height,
+ ( pSourceVTF->Flags() & TEXTUREFLAGS_SRGB ) ? true : false );
+ if ( !bRetVal )
+ {
+ return false;
+ }
+
+//X360TBD: incorrect byte order
+// // fixup mip dependent data
+// if ( ( pSourceVTF->Flags() & TEXTUREFLAGS_ONEOVERMIPLEVELINALPHA ) && ( targetFormat == IMAGE_FORMAT_BGRA8888 ) )
+// {
+// unsigned char ooMipLevel = ( unsigned char )( 255.0f * ( 1.0f / ( float )( 1 << mip ) ) );
+// int i;
+//
+// for ( i=0; i<width*height; i++ )
+// {
+// pTargetImage[i*4+3] = ooMipLevel;
+// }
+// }
+
+ targetSize = targetBuf.Size() + targetImageSize;
+ targetBuf.EnsureCapacity( targetSize );
+ targetBuf.Put( pTargetImage, targetImageSize );
+ if ( !targetBuf.IsValid() )
+ {
+ return false;
+ }
+
+ // success
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Generate the 360 target into a buffer
+//-----------------------------------------------------------------------------
+bool ConvertVTFTo360Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc )
+{
+ bool bRetVal;
+ IVTFTexture *pSourceVTF;
+ int targetWidth;
+ int targetHeight;
+ int targetMipCount;
+ VTFFileHeaderX360_t targetHeader;
+ int frame;
+ int face;
+ int mip;
+ ImageFormat targetFormat;
+ int targetLowResWidth;
+ int targetLowResHeight;
+ int targetFlags;
+ int mipSkipCount;
+ int targetFaceCount;
+ int preloadDataSize;
+ int targetImageDataOffset;
+ int targetFrameCount;
+ VTFFileHeaderV7_1_t *pVTFHeader71;
+ bool bNoMip;
+ CByteswap byteSwapWriter;
+ CUtlVector< ResourceCopy_t > targetResources;
+ bool bHasLowResData = false;
+ unsigned int resourceTypes[MAX_RSRC_DICTIONARY_ENTRIES];
+ unsigned char targetLowResSample[4];
+ int numTypes;
+
+ // Only need to byte swap writes if we are running the coversion on the PC, and data will be read from 360
+ byteSwapWriter.ActivateByteSwapping( !IsX360() );
+
+ // need mathlib
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+
+ // default failure
+ bRetVal = false;
+
+ pSourceVTF = NULL;
+
+ // unserialize the vtf with just the header
+ pSourceVTF = CreateVTFTexture();
+ if ( !pSourceVTF->Unserialize( sourceBuf, true, 0 ) )
+ goto cleanUp;
+
+ // volume textures not supported
+ if ( pSourceVTF->Depth() != 1 )
+ goto cleanUp;
+
+ if ( !ImageLoader::IsFormatValidForConversion( pSourceVTF->Format() ) )
+ goto cleanUp;
+
+ if ( !ComputeTargetDimensions( pDebugName, pSourceVTF, 0, targetWidth, targetHeight, targetMipCount, mipSkipCount, bNoMip ) )
+ goto cleanUp;
+
+ // must crack vtf file to determine if mip levels exist from header
+ // vtf interface does not expose the true presence of this data
+ pVTFHeader71 = (VTFFileHeaderV7_1_t*)sourceBuf.Base();
+ if ( mipSkipCount >= pVTFHeader71->numMipLevels )
+ {
+ // can't skip mips that aren't there
+ // ideally should just reconstruct them
+ goto cleanUp;
+ }
+
+ // unserialize the vtf with all the data configured with the desired starting mip
+ sourceBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ if ( !pSourceVTF->Unserialize( sourceBuf, false, mipSkipCount ) )
+ {
+ Msg( "ConvertVTFTo360Format: Error reading in %s\n", pDebugName );
+ goto cleanUp;
+ }
+
+ // add the default resource image
+ ResourceCopy_t resourceCopy;
+ resourceCopy.m_EntryInfo.eType = VTF_LEGACY_RSRC_IMAGE;
+ resourceCopy.m_EntryInfo.resData = 0;
+ resourceCopy.m_pData = NULL;
+ resourceCopy.m_DataLength = 0;
+ targetResources.AddToTail( resourceCopy );
+
+ // get the resources
+ numTypes = pSourceVTF->GetResourceTypes( resourceTypes, MAX_RSRC_DICTIONARY_ENTRIES );
+ for ( int i=0; i<numTypes; i++ )
+ {
+ size_t resourceLength;
+ void *pResourceData;
+
+ switch ( resourceTypes[i] & ~RSRCF_MASK )
+ {
+ case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
+ case VTF_LEGACY_RSRC_IMAGE:
+ case VTF_RSRC_TEXTURE_LOD_SETTINGS:
+ case VTF_RSRC_TEXTURE_SETTINGS_EX:
+ case VTF_RSRC_TEXTURE_CRC:
+ // not needed, presence already folded into conversion
+ continue;
+
+ default:
+ pResourceData = pSourceVTF->GetResourceData( resourceTypes[i], &resourceLength );
+ if ( pResourceData )
+ {
+ resourceCopy.m_EntryInfo.eType = resourceTypes[i] & ~RSRCF_MASK;
+ resourceCopy.m_EntryInfo.resData = 0;
+ resourceCopy.m_pData = new char[resourceLength];
+ resourceCopy.m_DataLength = resourceLength;
+ V_memcpy( resourceCopy.m_pData, pResourceData, resourceLength );
+ targetResources.AddToTail( resourceCopy );
+ }
+ break;
+ }
+ }
+
+ if ( targetResources.Count() > MAX_X360_RSRC_DICTIONARY_ENTRIES )
+ {
+ Msg( "ConvertVTFTo360Format: More resources than expected in %s\n", pDebugName );
+ goto cleanUp;
+ }
+
+ targetFlags = pSourceVTF->Flags();
+ targetFrameCount = pSourceVTF->FrameCount();
+
+ // skip over spheremap
+ targetFaceCount = pSourceVTF->FaceCount();
+ if ( targetFaceCount == CUBEMAP_FACE_COUNT )
+ {
+ targetFaceCount = CUBEMAP_FACE_COUNT-1;
+ }
+
+ // determine target format
+ targetFormat = PreferredFormat( pSourceVTF, pSourceVTF->Format(), targetWidth, targetHeight, targetMipCount, targetFaceCount );
+
+ // reset nomip flags
+ if ( bNoMip )
+ {
+ targetFlags |= TEXTUREFLAGS_NOMIP;
+ }
+ else
+ {
+ targetFlags &= ~TEXTUREFLAGS_NOMIP;
+ }
+
+ // the lowres texture is used for coarse light sampling lookups
+ bHasLowResData = ( pSourceVTF->LowResFormat() != -1 ) && pSourceVTF->LowResWidth() && pSourceVTF->LowResHeight();
+ if ( bHasLowResData )
+ {
+ // ensure lowres data is serialized in preferred runtime expected format
+ targetLowResWidth = pSourceVTF->LowResWidth();
+ targetLowResHeight = pSourceVTF->LowResHeight();
+ }
+ else
+ {
+ // discarding low res data, ensure lowres data is culled
+ targetLowResWidth = 0;
+ targetLowResHeight = 0;
+ }
+
+ // start serializing output data
+ // skip past header
+ // serialize in order, 0) Header 1) ResourceDictionary, 3) Resources, 4) image
+ // preload may extend into image
+ targetBuf.EnsureCapacity( sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
+ targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
+
+ // serialize low res
+ if ( targetLowResWidth && targetLowResHeight )
+ {
+ CUtlMemory<byte> targetLowResImage;
+
+ int sourceLowResImageSize = ImageLoader::GetMemRequired( pSourceVTF->LowResWidth(), pSourceVTF->LowResHeight(), 1, pSourceVTF->LowResFormat(), false );
+ int targetLowResImageSize = ImageLoader::GetMemRequired( targetLowResWidth, targetLowResHeight, 1, IMAGE_FORMAT_RGB888, false );
+
+ // conversion may skip bytes, ensure all bits initialized
+ targetLowResImage.EnsureCapacity( targetLowResImageSize );
+ byte* pTargetLowResImage = (byte*)targetLowResImage.Base();
+ memset( pTargetLowResImage, 0xFF, targetLowResImageSize );
+
+ // convert and save lowres image in final format
+ bRetVal = ConvertImageFormatEx(
+ pSourceVTF->LowResImageData(),
+ sourceLowResImageSize,
+ pSourceVTF->LowResFormat(),
+ pTargetLowResImage,
+ targetLowResImageSize,
+ IMAGE_FORMAT_RGB888,
+ targetLowResWidth,
+ targetLowResHeight,
+ false );
+ if ( !bRetVal )
+ {
+ goto cleanUp;
+ }
+
+ // boil to a single linear color
+ Vector linearColor;
+ linearColor.x = linearColor.y = linearColor.z = 0;
+ for ( int j = 0; j < targetLowResWidth * targetLowResHeight; j++ )
+ {
+ linearColor.x += SrgbGammaToLinear( pTargetLowResImage[j*3+0] * 1.0f/255.0f );
+ linearColor.y += SrgbGammaToLinear( pTargetLowResImage[j*3+1] * 1.0f/255.0f );
+ linearColor.z += SrgbGammaToLinear( pTargetLowResImage[j*3+2] * 1.0f/255.0f );
+ }
+ VectorScale( linearColor, 1.0f/(targetLowResWidth * targetLowResHeight), linearColor );
+
+ // serialize as a single texel
+ targetLowResSample[0] = 255.0f * SrgbLinearToGamma( linearColor[0] );
+ targetLowResSample[1] = 255.0f * SrgbLinearToGamma( linearColor[1] );
+ targetLowResSample[2] = 255.0f * SrgbLinearToGamma( linearColor[2] );
+
+ // identifies color presence
+ targetLowResSample[3] = 0xFF;
+ }
+ else
+ {
+ targetLowResSample[0] = 0;
+ targetLowResSample[1] = 0;
+ targetLowResSample[2] = 0;
+ targetLowResSample[3] = 0;
+ }
+
+ // serialize resource data
+ for ( int i=0; i<targetResources.Count(); i++ )
+ {
+ int resourceDataLength = targetResources[i].m_DataLength;
+ if ( resourceDataLength == 4 )
+ {
+ // data goes directly into structure, as is
+ targetResources[i].m_EntryInfo.eType |= RSRCF_HAS_NO_DATA_CHUNK;
+ V_memcpy( &targetResources[i].m_EntryInfo.resData, targetResources[i].m_pData, 4 );
+ }
+ else if ( resourceDataLength != 0 )
+ {
+ targetResources[i].m_EntryInfo.resData = targetBuf.TellPut();
+ int swappedLength = 0;
+ byteSwapWriter.SwapBufferToTargetEndian( &swappedLength, &resourceDataLength );
+ targetBuf.PutInt( swappedLength );
+ if ( !targetBuf.IsValid() )
+ {
+ goto cleanUp;
+ }
+
+ // put the data
+ targetBuf.Put( targetResources[i].m_pData, resourceDataLength );
+ if ( !targetBuf.IsValid() )
+ {
+ goto cleanUp;
+ }
+ }
+ }
+
+ // mark end of preload data
+ // preload data might be updated and pushed to extend into the image data mip chain
+ preloadDataSize = targetBuf.TellPut();
+
+ // image starts on an aligned boundary
+ AlignBuffer( targetBuf, 4 );
+
+ // start of image data
+ targetImageDataOffset = targetBuf.TellPut();
+ if ( targetImageDataOffset >= 65536 )
+ {
+ // possible bug, or may have to offset to 32 bits
+ Msg( "ConvertVTFTo360Format: non-image portion exceeds 16 bit boundary %s\n", pDebugName );
+ goto cleanUp;
+ }
+
+ // format conversion, data is stored by ascending mips, 1x1 up to NxN
+ // data is stored ascending to allow picmipped loads
+ for ( mip = targetMipCount - 1; mip >= 0; mip-- )
+ {
+ for ( frame = 0; frame < targetFrameCount; frame++ )
+ {
+ for ( face = 0; face < targetFaceCount; face++ )
+ {
+ if ( !SerializeImageData( pSourceVTF, frame, face, mip, targetFormat, targetBuf ) )
+ {
+ goto cleanUp;
+ }
+ }
+ }
+ }
+
+ if ( preloadDataSize < VTFFileHeaderSize( VTF_X360_MAJOR_VERSION, VTF_X360_MINOR_VERSION ) )
+ {
+ // preload size must be at least what game attempts to initially read
+ preloadDataSize = VTFFileHeaderSize( VTF_X360_MAJOR_VERSION, VTF_X360_MINOR_VERSION );
+ }
+
+ if ( targetBuf.TellPut() <= PRELOAD_VTF_THRESHOLD )
+ {
+ // the entire file is too small, preload entirely
+ preloadDataSize = targetBuf.TellPut();
+ }
+
+ if ( preloadDataSize >= 65536 )
+ {
+ // possible overflow due to large frames, faces, and format, may have to offset to 32 bits
+ Msg( "ConvertVTFTo360Format: preload portion exceeds 16 bit boundary %s\n", pDebugName );
+ goto cleanUp;
+ }
+
+ // finalize header
+ V_memset( &targetHeader, 0, sizeof( VTFFileHeaderX360_t ) );
+
+ V_memcpy( targetHeader.fileTypeString, "VTFX", 4 );
+ targetHeader.version[0] = VTF_X360_MAJOR_VERSION;
+ targetHeader.version[1] = VTF_X360_MINOR_VERSION;
+ targetHeader.headerSize = sizeof( VTFFileHeaderX360_t ) + targetResources.Count() * sizeof( ResourceEntryInfo );
+
+ targetHeader.flags = targetFlags;
+ targetHeader.width = targetWidth;
+ targetHeader.height = targetHeight;
+ targetHeader.depth = 1;
+ targetHeader.numFrames = targetFrameCount;
+ targetHeader.preloadDataSize = preloadDataSize;
+ targetHeader.mipSkipCount = mipSkipCount;
+ targetHeader.numResources = targetResources.Count();
+ VectorCopy( pSourceVTF->Reflectivity(), targetHeader.reflectivity );
+ targetHeader.bumpScale = pSourceVTF->BumpScale();
+ targetHeader.imageFormat = targetFormat;
+ targetHeader.lowResImageSample[0] = targetLowResSample[0];
+ targetHeader.lowResImageSample[1] = targetLowResSample[1];
+ targetHeader.lowResImageSample[2] = targetLowResSample[2];
+ targetHeader.lowResImageSample[3] = targetLowResSample[3];
+
+ if ( !IsX360() )
+ {
+ byteSwapWriter.SwapFieldsToTargetEndian( &targetHeader );
+ }
+
+ // write out finalized header
+ targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ targetBuf.Put( &targetHeader, sizeof( VTFFileHeaderX360_t ) );
+ if ( !targetBuf.IsValid() )
+ {
+ goto cleanUp;
+ }
+
+ // fixup and write out finalized resource dictionary
+ for ( int i=0; i<targetResources.Count(); i++ )
+ {
+ switch ( targetResources[i].m_EntryInfo.eType & ~RSRCF_MASK )
+ {
+ case VTF_LEGACY_RSRC_IMAGE:
+ targetResources[i].m_EntryInfo.resData = targetImageDataOffset;
+ break;
+ }
+
+ if ( !( targetResources[i].m_EntryInfo.eType & RSRCF_HAS_NO_DATA_CHUNK ) )
+ {
+ // swap the offset holders only
+ byteSwapWriter.SwapBufferToTargetEndian( &targetResources[i].m_EntryInfo.resData );
+ }
+
+ targetBuf.Put( &targetResources[i].m_EntryInfo, sizeof( ResourceEntryInfo ) );
+ if ( !targetBuf.IsValid() )
+ {
+ goto cleanUp;
+ }
+ }
+
+ targetBuf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
+
+ if ( preloadDataSize < targetBuf.TellPut() && pCompressFunc )
+ {
+ // only compress files that are not entirely in preload
+ CUtlBuffer compressedBuffer;
+ targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, targetImageDataOffset );
+ bool bCompressed = pCompressFunc( targetBuf, compressedBuffer );
+ if ( bCompressed )
+ {
+ // copy all the header data off
+ CUtlBuffer headerBuffer;
+ headerBuffer.EnsureCapacity( targetImageDataOffset );
+ headerBuffer.Put( targetBuf.Base(), targetImageDataOffset );
+
+ // reform the target with the header and then the compressed data
+ targetBuf.Clear();
+ targetBuf.Put( headerBuffer.Base(), targetImageDataOffset );
+ targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
+
+ VTFFileHeaderX360_t *pHeader = (VTFFileHeaderX360_t *)targetBuf.Base();
+ if ( !IsX360() )
+ {
+ // swap it back into pc space
+ byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
+ }
+
+ pHeader->compressedSize = compressedBuffer.TellPut();
+
+ if ( !IsX360() )
+ {
+ // swap it back into 360 space
+ byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
+ }
+ }
+
+ targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ }
+
+ // success
+ bRetVal = true;
+
+cleanUp:
+ if ( pSourceVTF )
+ {
+ DestroyVTFTexture( pSourceVTF );
+ }
+
+ for ( int i=0; i<targetResources.Count(); i++ )
+ {
+ delete [] (char *)targetResources[i].m_pData;
+ targetResources[i].m_pData = NULL;
+ }
+
+ return bRetVal;
+}
+
+//-----------------------------------------------------------------------------
+// Copy the 360 preload data into a buffer. Used by tools to request the preload,
+// as part of the preload build process. Caller doesn't have to know cracking details.
+// Not to be used at gametime.
+//-----------------------------------------------------------------------------
+bool GetVTFPreload360Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ preloadBufferOut.Purge();
+
+ fileBufferIn.ActivateByteSwapping( IsPC() );
+
+ VTFFileHeaderX360_t header;
+ fileBufferIn.GetObjects( &header );
+
+ if ( V_strnicmp( header.fileTypeString, "VTFX", 4 ) ||
+ header.version[0] != VTF_X360_MAJOR_VERSION ||
+ header.version[1] != VTF_X360_MINOR_VERSION )
+ {
+ // bad format
+ return false;
+ }
+
+ preloadBufferOut.EnsureCapacity( header.preloadDataSize );
+ preloadBufferOut.Put( fileBufferIn.Base(), header.preloadDataSize );
+
+ return true;
+}