|
|
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
//
// Purpose: Force pc .VTF to preferred .VTF 360 format conversion
//
//=====================================================================================//
#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"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.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, VtfConsoleFormatType_t targetConsole ) { 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_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_RGBA16161616: case IMAGE_FORMAT_RGBA16161616F: if ( targetConsole == VTF_CONSOLE_PS3 ) { return IMAGE_FORMAT_RGBA16161616F; } else { 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, int nMaxMip ) { width = pVTFTexture->Width(); height = pVTFTexture->Height(); ImageFormat format = pVTFTexture->Format();
// adhere to texture's internal lod setting
int nClampX = 1<<30; int nClampY = 1<<30;
if ( nMaxMip > 0 ) { // the specified maxmip specified by the caller trumps any embedded LOD
// cstrike has a merge ton of files with incorrect misspecified LODs from other products, etc. with no trivial way to discover or easily alter wholesale
// MGD is now adhering to a central script that specifies the texture LODs for faster precise control that can provide wholesale discovery etc.
nClampX = MIN( nClampX, nMaxMip ); nClampY = MIN( nClampY, nMaxMip ); } else { // a default maxmip defaults to smaller of 512x512 or optional embedded LOD
nClampX = MIN( nClampX, 512 ); nClampY = MIN( nClampY, 512 );
// no maxmip has been explicitly specified by caller, use embedded LOD setting if present
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_ResolutionClampY ) { nClampY = MIN( nClampY, 1 << pLODInfo->m_ResolutionClampY ); }
if ( pLODInfo->m_ResolutionClampX_360 ) { nClampX = MIN( nClampX, 1 << pLODInfo->m_ResolutionClampX_360 ); } if ( pLODInfo->m_ResolutionClampY_360 ) { nClampY = MIN( nClampY, 1 << pLODInfo->m_ResolutionClampY_360 ); } } }
// spin down to desired texture size
// not allowing top mips > 1MB
mipSkipCount = 0; while ( ( mipSkipCount < picmip ) || ( width > nClampX ) || ( height > nClampY ) || ( ImageLoader::GetMemRequired( width, height, 1, format, false ) > 1 * 1024 * 1024 ) ) { 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 an image from SRGB space to the 360 piecewise linear space.
//-----------------------------------------------------------------------------
bool SRGBCorrectImage( unsigned char *pImage, int imageSize, ImageFormat imageFormat) { if ( imageFormat == IMAGE_FORMAT_BGRA8888 || imageFormat == IMAGE_FORMAT_BGRX8888 || imageFormat == IMAGE_FORMAT_RGBA8888) { //Msg( " Converting 8888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
for ( int i = 0; i < ( imageSize / 4 ); i++ ) // imageSize is the raw data length in bytes
{ unsigned char *pRGB[3] = { NULL, NULL, NULL };
pRGB[0] = &( pImage[ ( i * 4 ) + 0 ] ); pRGB[1] = &( pImage[ ( i * 4 ) + 1 ] ); pRGB[2] = &( pImage[ ( i * 4 ) + 2 ] ); // 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( imageFormat == IMAGE_FORMAT_BGR888) { //Msg( " Converting 888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
for ( int i = 0; i < ( imageSize / 3 ); i++ ) // imageSize is the raw data length in bytes
{ unsigned char *pRGB[3] = { NULL, NULL, NULL };
pRGB[0] = &( pImage[ ( i * 3 ) + 0 ] ); // Blue
pRGB[1] = &( pImage[ ( i * 3 ) + 1 ] ); // Green
pRGB[2] = &( pImage[ ( i * 3 ) + 2 ] ); // Red
// 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 { Msg("Unknown format:%d In SRGBCorrectImage\n",imageFormat); } return true; }
//-----------------------------------------------------------------------------
// Swizzle PS3 image data
//-----------------------------------------------------------------------------
static inline uint32 Ps3Helper_LinearToSwizzleAddress( uint16 c[3], uint16 size[3] ) { uint32 offset = 0; uint32 uOffsetBit = 1; do { for ( int jj = 0; jj < 3; ++ jj ) { if ( ( size[jj] >>= 1 ) != 0 ) { offset += ( c[jj] & 1 ) ? uOffsetBit : 0; uOffsetBit <<= 1; c[jj] >>= 1; } } } while ( c[0]+c[1]+c[2] ); return offset; } void PostConvertSwizzleImageData( unsigned char *pTargetImage, int targetImageSize, ImageFormat targetFormat, int width, int height, VtfConsoleFormatType_t targetConsole ) { // Only applicable for PS3
if ( targetConsole != VTF_CONSOLE_PS3 ) return;
// Only applicable for power-of-two textures
if ( !IsPowerOfTwo( width ) || !IsPowerOfTwo( height ) ) return;
// Not applicable for DXT
switch ( targetFormat ) { case IMAGE_FORMAT_DXT1: case IMAGE_FORMAT_DXT3: case IMAGE_FORMAT_DXT5: return; }
unsigned int numBlockBytes = ImageLoader::GetMemRequired( 1, 1, 1, targetFormat, false ); if ( numBlockBytes > 4 ) { width *= ( numBlockBytes / 4 ); numBlockBytes = 4; } unsigned char *pSwizzleData = (unsigned char *)malloc( targetImageSize ); int depth = 1; for ( uint16 x = 0; x < width; ++ x ) for ( uint16 y = 0; y < height; ++ y ) for ( uint16 z = 0; z < depth; ++ z ) { uint16 c[3] = {x,y,z}; uint16 sizeArg[3] = { width, height, depth }; uint32 uiSwizzleOffset = Ps3Helper_LinearToSwizzleAddress( c, sizeArg ); memcpy( pSwizzleData + (uiSwizzleOffset*numBlockBytes), pTargetImage+(y*width+x)*numBlockBytes, numBlockBytes ); } memcpy( pTargetImage, pSwizzleData, targetImageSize ); free( pSwizzleData ); }
//-----------------------------------------------------------------------------
// 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, VtfConsoleFormatType_t targetConsole ) { // 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, targetConsole );
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, targetConsole ); PostConvertSwizzleImageData( pTargetImage, targetImageSize, targetFormat, width, height, targetConsole );
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, VtfConsoleFormatType_t targetConsole ) { 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, targetConsole ); 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
//-----------------------------------------------------------------------------
template< VtfConsoleFormatType_t e > struct ConvertVTFToConsoleFormatHelper_t { ; }; template<> struct ConvertVTFToConsoleFormatHelper_t< VTF_CONSOLE_360 > { typedef VTFFileHeaderX360_t Type; enum Const_t { VER_MAJOR = VTF_X360_MAJOR_VERSION, VER_MINOR = VTF_X360_MINOR_VERSION }; static char const * GetHeaderType() { return "VTFX"; } }; template<> struct ConvertVTFToConsoleFormatHelper_t< VTF_CONSOLE_PS3 > { typedef VTFFileHeaderPS3_t Type; enum Const_t { VER_MAJOR = VTF_PS3_MAJOR_VERSION, VER_MINOR = VTF_PS3_MINOR_VERSION }; static char const * GetHeaderType() { return "VTF3"; } }; template < VtfConsoleFormatType_t eConFmtType > bool ConvertVTFToConsoleFormatHelper( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip ) { bool bRetVal; IVTFTexture *pSourceVTF; int targetWidth; int targetHeight; int targetMipCount; typedef ConvertVTFToConsoleFormatHelper_t< eConFmtType > TargetHelper; typedef typename TargetHelper::Type TargetHeaderType; TargetHeaderType 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 console
byteSwapWriter.ActivateByteSwapping( !IsGameConsole() );
// 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, nMaxMip ) ) 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( "ConvertVTFToConsoleFormatHelper: 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]; Assert( resourceLength == ( int )resourceLength ); resourceCopy.m_DataLength = ( int )resourceLength; V_memcpy( resourceCopy.m_pData, pResourceData, resourceLength ); targetResources.AddToTail( resourceCopy ); } break; } }
if ( targetResources.Count() > MAX_X360_RSRC_DICTIONARY_ENTRIES ) { Msg( "ConvertVTFToConsoleFormatHelper: More resources than expected in %s\n", pDebugName ); goto cleanUp; }
targetFlags = pSourceVTF->Flags(); targetFrameCount = pSourceVTF->FrameCount(); targetFaceCount = pSourceVTF->FaceCount();
// determine target format
targetFormat = PreferredFormat( pSourceVTF, pSourceVTF->Format(), targetWidth, targetHeight, targetMipCount, targetFaceCount, eConFmtType );
// 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( targetHeader ) + targetResources.Count() * sizeof( ResourceEntryInfo ) ); targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, sizeof( targetHeader ) + 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, eConFmtType ); 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; 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( "ConvertVTFToConsoleFormatHelper: 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, eConFmtType ) ) { goto cleanUp; } } } }
if ( preloadDataSize < VTFFileHeaderSize( TargetHelper::VER_MAJOR, TargetHelper::VER_MINOR ) ) { // preload size must be at least what game attempts to initially read
preloadDataSize = VTFFileHeaderSize( TargetHelper::VER_MAJOR, TargetHelper::VER_MINOR ); }
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( "ConvertVTFToConsoleFormatHelper: preload portion exceeds 16 bit boundary %s\n", pDebugName ); goto cleanUp; }
// finalize header
V_memset( &targetHeader, 0, sizeof( targetHeader ) );
V_memcpy( targetHeader.fileTypeString, TargetHelper::GetHeaderType(), 4 ); targetHeader.version[0] = TargetHelper::VER_MAJOR; targetHeader.version[1] = TargetHelper::VER_MINOR; targetHeader.headerSize = sizeof( targetHeader ) + 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 ( !IsGameConsole() ) { byteSwapWriter.SwapFieldsToTargetEndian( &targetHeader ); }
// write out finalized header
targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); targetBuf.Put( &targetHeader, sizeof( targetHeader ) ); 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() );
TargetHeaderType *pHeader = (TargetHeaderType *)targetBuf.Base(); if ( !IsGameConsole() ) { // swap it back into pc space
byteSwapWriter.SwapFieldsToTargetEndian( pHeader ); }
pHeader->compressedSize = compressedBuffer.TellPut();
if ( !IsGameConsole() ) { // swap it back into console 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; }
bool ConvertVTFTo360Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip ) { return ConvertVTFToConsoleFormatHelper< VTF_CONSOLE_360 >( pDebugName, sourceBuf, targetBuf, pCompressFunc, nMaxMip ); }
bool ConvertVTFToPS3Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip ) { return ConvertVTFToConsoleFormatHelper< VTF_CONSOLE_PS3 >( pDebugName, sourceBuf, targetBuf, pCompressFunc, nMaxMip ); }
//-----------------------------------------------------------------------------
// 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; }
//-----------------------------------------------------------------------------
// Copy the PS3 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 GetVTFPreloadPS3Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut ) { preloadBufferOut.Purge();
fileBufferIn.ActivateByteSwapping( IsPC() );
VTFFileHeaderPS3_t header; fileBufferIn.GetObjects( &header );
if ( V_strnicmp( header.fileTypeString, "VTF3", 4 ) || header.version[0] != VTF_PS3_MAJOR_VERSION || header.version[1] != VTF_PS3_MINOR_VERSION ) { // bad format
return false; }
preloadBufferOut.EnsureCapacity( header.preloadDataSize ); preloadBufferOut.Put( fileBufferIn.Base(), header.preloadDataSize );
return true; }
|