Counter Strike : Global Offensive Source Code
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.

937 lines
29 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. //
  4. // Purpose: Force pc .VTF to preferred .VTF 360 format conversion
  5. //
  6. //=====================================================================================//
  7. #include "mathlib/mathlib.h"
  8. #include "tier1/strtools.h"
  9. #include "cvtf.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "tier0/dbg.h"
  12. #include "tier1/utlmemory.h"
  13. #include "bitmap/imageformat.h"
  14. // NOTE: This has to be the last file included!
  15. #include "tier0/memdbgon.h"
  16. // if the entire vtf file is smaller than this threshold, add entirely to preload
  17. #define PRELOAD_VTF_THRESHOLD 2048
  18. struct ResourceCopy_t
  19. {
  20. void *m_pData;
  21. int m_DataLength;
  22. ResourceEntryInfo m_EntryInfo;
  23. };
  24. //-----------------------------------------------------------------------------
  25. // Converts to an alternate format
  26. //-----------------------------------------------------------------------------
  27. ImageFormat PreferredFormat( IVTFTexture *pVTFTexture, ImageFormat fmt, int width, int height, int mipCount, int faceCount, VtfConsoleFormatType_t targetConsole )
  28. {
  29. switch ( fmt )
  30. {
  31. case IMAGE_FORMAT_RGBA8888:
  32. case IMAGE_FORMAT_ABGR8888:
  33. case IMAGE_FORMAT_ARGB8888:
  34. case IMAGE_FORMAT_BGRA8888:
  35. return IMAGE_FORMAT_BGRA8888;
  36. // 24bpp gpu formats don't exist, must convert
  37. case IMAGE_FORMAT_BGRX8888:
  38. case IMAGE_FORMAT_RGB888:
  39. case IMAGE_FORMAT_BGR888:
  40. case IMAGE_FORMAT_RGB888_BLUESCREEN:
  41. case IMAGE_FORMAT_BGR888_BLUESCREEN:
  42. return IMAGE_FORMAT_BGRX8888;
  43. case IMAGE_FORMAT_BGRX5551:
  44. case IMAGE_FORMAT_RGB565:
  45. case IMAGE_FORMAT_BGR565:
  46. return IMAGE_FORMAT_BGR565;
  47. // no change
  48. case IMAGE_FORMAT_I8:
  49. case IMAGE_FORMAT_IA88:
  50. case IMAGE_FORMAT_A8:
  51. case IMAGE_FORMAT_BGRA4444:
  52. case IMAGE_FORMAT_BGRA5551:
  53. case IMAGE_FORMAT_UV88:
  54. case IMAGE_FORMAT_UVWQ8888:
  55. case IMAGE_FORMAT_UVLX8888:
  56. case IMAGE_FORMAT_DXT1_ONEBITALPHA:
  57. case IMAGE_FORMAT_DXT1:
  58. case IMAGE_FORMAT_DXT3:
  59. case IMAGE_FORMAT_DXT5:
  60. case IMAGE_FORMAT_ATI1N:
  61. case IMAGE_FORMAT_ATI2N:
  62. break;
  63. case IMAGE_FORMAT_RGBA16161616:
  64. case IMAGE_FORMAT_RGBA16161616F:
  65. if ( targetConsole == VTF_CONSOLE_PS3 )
  66. {
  67. return IMAGE_FORMAT_RGBA16161616F;
  68. }
  69. else
  70. {
  71. return IMAGE_FORMAT_RGBA16161616;
  72. }
  73. }
  74. return fmt;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Determines target dimensions
  78. //-----------------------------------------------------------------------------
  79. bool ComputeTargetDimensions( const char *pDebugName, IVTFTexture *pVTFTexture, int picmip, int &width, int &height, int &mipCount, int &mipSkipCount, bool &bNoMip, int nMaxMip )
  80. {
  81. width = pVTFTexture->Width();
  82. height = pVTFTexture->Height();
  83. ImageFormat format = pVTFTexture->Format();
  84. // adhere to texture's internal lod setting
  85. int nClampX = 1<<30;
  86. int nClampY = 1<<30;
  87. if ( nMaxMip > 0 )
  88. {
  89. // the specified maxmip specified by the caller trumps any embedded LOD
  90. // 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
  91. // MGD is now adhering to a central script that specifies the texture LODs for faster precise control that can provide wholesale discovery etc.
  92. nClampX = MIN( nClampX, nMaxMip );
  93. nClampY = MIN( nClampY, nMaxMip );
  94. }
  95. else
  96. {
  97. // a default maxmip defaults to smaller of 512x512 or optional embedded LOD
  98. nClampX = MIN( nClampX, 512 );
  99. nClampY = MIN( nClampY, 512 );
  100. // no maxmip has been explicitly specified by caller, use embedded LOD setting if present
  101. TextureLODControlSettings_t const *pLODInfo = reinterpret_cast<TextureLODControlSettings_t const *> ( pVTFTexture->GetResourceData( VTF_RSRC_TEXTURE_LOD_SETTINGS, NULL ) );
  102. if ( pLODInfo )
  103. {
  104. if ( pLODInfo->m_ResolutionClampX )
  105. {
  106. nClampX = MIN( nClampX, 1 << pLODInfo->m_ResolutionClampX );
  107. }
  108. if ( pLODInfo->m_ResolutionClampY )
  109. {
  110. nClampY = MIN( nClampY, 1 << pLODInfo->m_ResolutionClampY );
  111. }
  112. if ( pLODInfo->m_ResolutionClampX_360 )
  113. {
  114. nClampX = MIN( nClampX, 1 << pLODInfo->m_ResolutionClampX_360 );
  115. }
  116. if ( pLODInfo->m_ResolutionClampY_360 )
  117. {
  118. nClampY = MIN( nClampY, 1 << pLODInfo->m_ResolutionClampY_360 );
  119. }
  120. }
  121. }
  122. // spin down to desired texture size
  123. // not allowing top mips > 1MB
  124. mipSkipCount = 0;
  125. while ( ( mipSkipCount < picmip ) ||
  126. ( width > nClampX ) ||
  127. ( height > nClampY ) ||
  128. ( ImageLoader::GetMemRequired( width, height, 1, format, false ) > 1 * 1024 * 1024 ) )
  129. {
  130. if ( width == 1 && height == 1 )
  131. break;
  132. width >>= 1;
  133. height >>= 1;
  134. if ( width < 1 )
  135. width = 1;
  136. if ( height < 1 )
  137. height = 1;
  138. mipSkipCount++;
  139. }
  140. bNoMip = false;
  141. if ( pVTFTexture->Flags() & TEXTUREFLAGS_NOMIP )
  142. {
  143. bNoMip = true;
  144. }
  145. // determine mip quantity based on desired width/height
  146. if ( bNoMip )
  147. {
  148. // avoid serializing unused mips
  149. mipCount = 1;
  150. }
  151. else
  152. {
  153. mipCount = ImageLoader::GetNumMipMapLevels( width, height );
  154. }
  155. // success
  156. return true;
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Align the buffer to specified boundary
  160. //-----------------------------------------------------------------------------
  161. int AlignBuffer( CUtlBuffer &buf, int alignment )
  162. {
  163. int curPosition;
  164. int newPosition;
  165. byte padByte = 0;
  166. // advance to aligned position
  167. buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
  168. curPosition = buf.TellPut();
  169. newPosition = AlignValue( curPosition, alignment );
  170. buf.EnsureCapacity( newPosition );
  171. // write empty
  172. for ( int i=0; i<newPosition-curPosition; i++ )
  173. {
  174. buf.Put( &padByte, 1 );
  175. }
  176. return newPosition;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Convert an image from SRGB space to the 360 piecewise linear space.
  180. //-----------------------------------------------------------------------------
  181. bool SRGBCorrectImage(
  182. unsigned char *pImage,
  183. int imageSize,
  184. ImageFormat imageFormat)
  185. {
  186. if ( imageFormat == IMAGE_FORMAT_BGRA8888 || imageFormat == IMAGE_FORMAT_BGRX8888 || imageFormat == IMAGE_FORMAT_RGBA8888)
  187. {
  188. //Msg( " Converting 8888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
  189. for ( int i = 0; i < ( imageSize / 4 ); i++ ) // imageSize is the raw data length in bytes
  190. {
  191. unsigned char *pRGB[3] = { NULL, NULL, NULL };
  192. pRGB[0] = &( pImage[ ( i * 4 ) + 0 ] );
  193. pRGB[1] = &( pImage[ ( i * 4 ) + 1 ] );
  194. pRGB[2] = &( pImage[ ( i * 4 ) + 2 ] );
  195. // Modify RGB data in place
  196. for ( int j = 0; j < 3; j++ ) // For red, green, blue
  197. {
  198. float flSrgbGamma = float( *( pRGB[j] ) ) / 255.0f;
  199. float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
  200. fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
  201. *( pRGB[j] ) = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
  202. }
  203. }
  204. }
  205. else if( imageFormat == IMAGE_FORMAT_BGR888)
  206. {
  207. //Msg( " Converting 888 texture from sRGB gamma to 360 PWL gamma *** %dx%d\n", width, height );
  208. for ( int i = 0; i < ( imageSize / 3 ); i++ ) // imageSize is the raw data length in bytes
  209. {
  210. unsigned char *pRGB[3] = { NULL, NULL, NULL };
  211. pRGB[0] = &( pImage[ ( i * 3 ) + 0 ] ); // Blue
  212. pRGB[1] = &( pImage[ ( i * 3 ) + 1 ] ); // Green
  213. pRGB[2] = &( pImage[ ( i * 3 ) + 2 ] ); // Red
  214. // Modify RGB data in place
  215. for ( int j = 0; j < 3; j++ ) // For red, green, blue
  216. {
  217. float flSrgbGamma = float( *( pRGB[j] ) ) / 255.0f;
  218. float fl360Gamma = SrgbGammaTo360Gamma( flSrgbGamma );
  219. fl360Gamma = clamp( fl360Gamma, 0.0f, 1.0f );
  220. *( pRGB[j] ) = ( unsigned char ) ( clamp( ( ( fl360Gamma * 255.0f ) + 0.5f ), 0.0f, 255.0f ) );
  221. }
  222. }
  223. }
  224. else
  225. {
  226. Msg("Unknown format:%d In SRGBCorrectImage\n",imageFormat);
  227. }
  228. return true;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Swizzle PS3 image data
  232. //-----------------------------------------------------------------------------
  233. static inline uint32 Ps3Helper_LinearToSwizzleAddress( uint16 c[3], uint16 size[3] )
  234. {
  235. uint32 offset = 0;
  236. uint32 uOffsetBit = 1;
  237. do
  238. {
  239. for ( int jj = 0; jj < 3; ++ jj )
  240. {
  241. if ( ( size[jj] >>= 1 ) != 0 )
  242. {
  243. offset += ( c[jj] & 1 ) ? uOffsetBit : 0;
  244. uOffsetBit <<= 1;
  245. c[jj] >>= 1;
  246. }
  247. }
  248. } while ( c[0]+c[1]+c[2] );
  249. return offset;
  250. }
  251. void PostConvertSwizzleImageData(
  252. unsigned char *pTargetImage,
  253. int targetImageSize,
  254. ImageFormat targetFormat,
  255. int width, int height,
  256. VtfConsoleFormatType_t targetConsole
  257. )
  258. {
  259. // Only applicable for PS3
  260. if ( targetConsole != VTF_CONSOLE_PS3 ) return;
  261. // Only applicable for power-of-two textures
  262. if ( !IsPowerOfTwo( width ) || !IsPowerOfTwo( height ) ) return;
  263. // Not applicable for DXT
  264. switch ( targetFormat )
  265. {
  266. case IMAGE_FORMAT_DXT1:
  267. case IMAGE_FORMAT_DXT3:
  268. case IMAGE_FORMAT_DXT5:
  269. return;
  270. }
  271. unsigned int numBlockBytes = ImageLoader::GetMemRequired( 1, 1, 1, targetFormat, false );
  272. if ( numBlockBytes > 4 )
  273. {
  274. width *= ( numBlockBytes / 4 );
  275. numBlockBytes = 4;
  276. }
  277. unsigned char *pSwizzleData = (unsigned char *)malloc( targetImageSize );
  278. int depth = 1;
  279. for ( uint16 x = 0; x < width; ++ x )
  280. for ( uint16 y = 0; y < height; ++ y )
  281. for ( uint16 z = 0; z < depth; ++ z )
  282. {
  283. uint16 c[3] = {x,y,z};
  284. uint16 sizeArg[3] = { width, height, depth };
  285. uint32 uiSwizzleOffset = Ps3Helper_LinearToSwizzleAddress( c, sizeArg );
  286. memcpy( pSwizzleData + (uiSwizzleOffset*numBlockBytes),
  287. pTargetImage+(y*width+x)*numBlockBytes, numBlockBytes );
  288. }
  289. memcpy( pTargetImage, pSwizzleData, targetImageSize );
  290. free( pSwizzleData );
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Convert the x86 image data to 360
  294. //-----------------------------------------------------------------------------
  295. bool ConvertImageFormatEx(
  296. unsigned char *pSourceImage,
  297. int sourceImageSize,
  298. ImageFormat sourceFormat,
  299. unsigned char *pTargetImage,
  300. int targetImageSize,
  301. ImageFormat targetFormat,
  302. int width,
  303. int height,
  304. VtfConsoleFormatType_t targetConsole )
  305. {
  306. // format conversion expects pc oriented data
  307. // but, formats that are >8 bits per channels need to be element pre-swapped
  308. ImageLoader::PreConvertSwapImageData( pSourceImage, sourceImageSize, sourceFormat, targetConsole );
  309. bool bRetVal = ImageLoader::ConvertImageFormat(
  310. pSourceImage,
  311. sourceFormat,
  312. pTargetImage,
  313. targetFormat,
  314. width,
  315. height );
  316. if ( !bRetVal )
  317. {
  318. return false;
  319. }
  320. // convert to proper channel order for 360 d3dformats
  321. ImageLoader::PostConvertSwapImageData( pTargetImage, targetImageSize, targetFormat, targetConsole );
  322. PostConvertSwizzleImageData( pTargetImage, targetImageSize, targetFormat, width, height, targetConsole );
  323. return true;
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Write the source data as the desired format into a target buffer
  327. //-----------------------------------------------------------------------------
  328. bool SerializeImageData( IVTFTexture *pSourceVTF, int frame, int face, int mip, ImageFormat targetFormat, CUtlBuffer &targetBuf, VtfConsoleFormatType_t targetConsole )
  329. {
  330. int width;
  331. int height;
  332. int targetImageSize;
  333. byte *pSourceImage;
  334. int sourceImageSize;
  335. int targetSize;
  336. CUtlMemory<byte> targetImage;
  337. width = pSourceVTF->Width() >> mip;
  338. height = pSourceVTF->Height() >> mip;
  339. if ( width < 1 )
  340. width = 1;
  341. if ( height < 1)
  342. height = 1;
  343. sourceImageSize = ImageLoader::GetMemRequired( width, height, 1, pSourceVTF->Format(), false );
  344. pSourceImage = pSourceVTF->ImageData( frame, face, mip );
  345. targetImageSize = ImageLoader::GetMemRequired( width, height, 1, targetFormat, false );
  346. targetImage.EnsureCapacity( targetImageSize );
  347. byte *pTargetImage = (byte*)targetImage.Base();
  348. // conversion may skip bytes, ensure all bits initialized
  349. memset( pTargetImage, 0xFF, targetImageSize );
  350. // format conversion expects pc oriented data
  351. bool bRetVal = ConvertImageFormatEx(
  352. pSourceImage,
  353. sourceImageSize,
  354. pSourceVTF->Format(),
  355. pTargetImage,
  356. targetImageSize,
  357. targetFormat,
  358. width,
  359. height,
  360. targetConsole );
  361. if ( !bRetVal )
  362. {
  363. return false;
  364. }
  365. //X360TBD: incorrect byte order
  366. // // fixup mip dependent data
  367. // if ( ( pSourceVTF->Flags() & TEXTUREFLAGS_ONEOVERMIPLEVELINALPHA ) && ( targetFormat == IMAGE_FORMAT_BGRA8888 ) )
  368. // {
  369. // unsigned char ooMipLevel = ( unsigned char )( 255.0f * ( 1.0f / ( float )( 1 << mip ) ) );
  370. // int i;
  371. //
  372. // for ( i=0; i<width*height; i++ )
  373. // {
  374. // pTargetImage[i*4+3] = ooMipLevel;
  375. // }
  376. // }
  377. targetSize = targetBuf.Size() + targetImageSize;
  378. targetBuf.EnsureCapacity( targetSize );
  379. targetBuf.Put( pTargetImage, targetImageSize );
  380. if ( !targetBuf.IsValid() )
  381. {
  382. return false;
  383. }
  384. // success
  385. return true;
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Generate the 360 target into a buffer
  389. //-----------------------------------------------------------------------------
  390. template< VtfConsoleFormatType_t e > struct ConvertVTFToConsoleFormatHelper_t
  391. {
  392. ;
  393. };
  394. template<> struct ConvertVTFToConsoleFormatHelper_t< VTF_CONSOLE_360 >
  395. {
  396. typedef VTFFileHeaderX360_t Type;
  397. enum Const_t { VER_MAJOR = VTF_X360_MAJOR_VERSION, VER_MINOR = VTF_X360_MINOR_VERSION };
  398. static char const * GetHeaderType() { return "VTFX"; }
  399. };
  400. template<> struct ConvertVTFToConsoleFormatHelper_t< VTF_CONSOLE_PS3 >
  401. {
  402. typedef VTFFileHeaderPS3_t Type;
  403. enum Const_t { VER_MAJOR = VTF_PS3_MAJOR_VERSION, VER_MINOR = VTF_PS3_MINOR_VERSION };
  404. static char const * GetHeaderType() { return "VTF3"; }
  405. };
  406. template < VtfConsoleFormatType_t eConFmtType >
  407. bool ConvertVTFToConsoleFormatHelper( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip )
  408. {
  409. bool bRetVal;
  410. IVTFTexture *pSourceVTF;
  411. int targetWidth;
  412. int targetHeight;
  413. int targetMipCount;
  414. typedef ConvertVTFToConsoleFormatHelper_t< eConFmtType > TargetHelper;
  415. typedef typename TargetHelper::Type TargetHeaderType;
  416. TargetHeaderType targetHeader;
  417. int frame;
  418. int face;
  419. int mip;
  420. ImageFormat targetFormat;
  421. int targetLowResWidth;
  422. int targetLowResHeight;
  423. int targetFlags;
  424. int mipSkipCount;
  425. int targetFaceCount;
  426. int preloadDataSize;
  427. int targetImageDataOffset;
  428. int targetFrameCount;
  429. VTFFileHeaderV7_1_t *pVTFHeader71;
  430. bool bNoMip;
  431. CByteswap byteSwapWriter;
  432. CUtlVector< ResourceCopy_t > targetResources;
  433. bool bHasLowResData = false;
  434. unsigned int resourceTypes[MAX_RSRC_DICTIONARY_ENTRIES];
  435. unsigned char targetLowResSample[4];
  436. int numTypes;
  437. // Only need to byte swap writes if we are running the coversion on the PC, and data will be read from console
  438. byteSwapWriter.ActivateByteSwapping( !IsGameConsole() );
  439. // need mathlib
  440. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  441. // default failure
  442. bRetVal = false;
  443. pSourceVTF = NULL;
  444. // unserialize the vtf with just the header
  445. pSourceVTF = CreateVTFTexture();
  446. if ( !pSourceVTF->Unserialize( sourceBuf, true, 0 ) )
  447. goto cleanUp;
  448. // volume textures not supported
  449. if ( pSourceVTF->Depth() != 1 )
  450. goto cleanUp;
  451. if ( !ImageLoader::IsFormatValidForConversion( pSourceVTF->Format() ) )
  452. goto cleanUp;
  453. if ( !ComputeTargetDimensions( pDebugName, pSourceVTF, 0, targetWidth, targetHeight, targetMipCount, mipSkipCount, bNoMip, nMaxMip ) )
  454. goto cleanUp;
  455. // must crack vtf file to determine if mip levels exist from header
  456. // vtf interface does not expose the true presence of this data
  457. pVTFHeader71 = (VTFFileHeaderV7_1_t*)sourceBuf.Base();
  458. if ( mipSkipCount >= pVTFHeader71->numMipLevels )
  459. {
  460. // can't skip mips that aren't there
  461. // ideally should just reconstruct them
  462. goto cleanUp;
  463. }
  464. // unserialize the vtf with all the data configured with the desired starting mip
  465. sourceBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  466. if ( !pSourceVTF->Unserialize( sourceBuf, false, mipSkipCount ) )
  467. {
  468. Msg( "ConvertVTFToConsoleFormatHelper: Error reading in %s\n", pDebugName );
  469. goto cleanUp;
  470. }
  471. // add the default resource image
  472. ResourceCopy_t resourceCopy;
  473. resourceCopy.m_EntryInfo.eType = VTF_LEGACY_RSRC_IMAGE;
  474. resourceCopy.m_EntryInfo.resData = 0;
  475. resourceCopy.m_pData = NULL;
  476. resourceCopy.m_DataLength = 0;
  477. targetResources.AddToTail( resourceCopy );
  478. // get the resources
  479. numTypes = pSourceVTF->GetResourceTypes( resourceTypes, MAX_RSRC_DICTIONARY_ENTRIES );
  480. for ( int i=0; i<numTypes; i++ )
  481. {
  482. size_t resourceLength;
  483. void *pResourceData;
  484. switch ( resourceTypes[i] & ~RSRCF_MASK )
  485. {
  486. case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
  487. case VTF_LEGACY_RSRC_IMAGE:
  488. case VTF_RSRC_TEXTURE_LOD_SETTINGS:
  489. case VTF_RSRC_TEXTURE_SETTINGS_EX:
  490. case VTF_RSRC_TEXTURE_CRC:
  491. // not needed, presence already folded into conversion
  492. continue;
  493. default:
  494. pResourceData = pSourceVTF->GetResourceData( resourceTypes[i], &resourceLength );
  495. if ( pResourceData )
  496. {
  497. resourceCopy.m_EntryInfo.eType = resourceTypes[i] & ~RSRCF_MASK;
  498. resourceCopy.m_EntryInfo.resData = 0;
  499. resourceCopy.m_pData = new char[resourceLength];
  500. Assert( resourceLength == ( int )resourceLength );
  501. resourceCopy.m_DataLength = ( int )resourceLength;
  502. V_memcpy( resourceCopy.m_pData, pResourceData, resourceLength );
  503. targetResources.AddToTail( resourceCopy );
  504. }
  505. break;
  506. }
  507. }
  508. if ( targetResources.Count() > MAX_X360_RSRC_DICTIONARY_ENTRIES )
  509. {
  510. Msg( "ConvertVTFToConsoleFormatHelper: More resources than expected in %s\n", pDebugName );
  511. goto cleanUp;
  512. }
  513. targetFlags = pSourceVTF->Flags();
  514. targetFrameCount = pSourceVTF->FrameCount();
  515. targetFaceCount = pSourceVTF->FaceCount();
  516. // determine target format
  517. targetFormat = PreferredFormat( pSourceVTF, pSourceVTF->Format(), targetWidth, targetHeight, targetMipCount, targetFaceCount, eConFmtType );
  518. // reset nomip flags
  519. if ( bNoMip )
  520. {
  521. targetFlags |= TEXTUREFLAGS_NOMIP;
  522. }
  523. else
  524. {
  525. targetFlags &= ~TEXTUREFLAGS_NOMIP;
  526. }
  527. // the lowres texture is used for coarse light sampling lookups
  528. bHasLowResData = ( pSourceVTF->LowResFormat() != -1 ) && pSourceVTF->LowResWidth() && pSourceVTF->LowResHeight();
  529. if ( bHasLowResData )
  530. {
  531. // ensure lowres data is serialized in preferred runtime expected format
  532. targetLowResWidth = pSourceVTF->LowResWidth();
  533. targetLowResHeight = pSourceVTF->LowResHeight();
  534. }
  535. else
  536. {
  537. // discarding low res data, ensure lowres data is culled
  538. targetLowResWidth = 0;
  539. targetLowResHeight = 0;
  540. }
  541. // start serializing output data
  542. // skip past header
  543. // serialize in order, 0) Header 1) ResourceDictionary, 3) Resources, 4) image
  544. // preload may extend into image
  545. targetBuf.EnsureCapacity( sizeof( targetHeader ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
  546. targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, sizeof( targetHeader ) + targetResources.Count() * sizeof( ResourceEntryInfo ) );
  547. // serialize low res
  548. if ( targetLowResWidth && targetLowResHeight )
  549. {
  550. CUtlMemory<byte> targetLowResImage;
  551. int sourceLowResImageSize = ImageLoader::GetMemRequired( pSourceVTF->LowResWidth(), pSourceVTF->LowResHeight(), 1, pSourceVTF->LowResFormat(), false );
  552. int targetLowResImageSize = ImageLoader::GetMemRequired( targetLowResWidth, targetLowResHeight, 1, IMAGE_FORMAT_RGB888, false );
  553. // conversion may skip bytes, ensure all bits initialized
  554. targetLowResImage.EnsureCapacity( targetLowResImageSize );
  555. byte* pTargetLowResImage = (byte*)targetLowResImage.Base();
  556. memset( pTargetLowResImage, 0xFF, targetLowResImageSize );
  557. // convert and save lowres image in final format
  558. bRetVal = ConvertImageFormatEx(
  559. pSourceVTF->LowResImageData(),
  560. sourceLowResImageSize,
  561. pSourceVTF->LowResFormat(),
  562. pTargetLowResImage,
  563. targetLowResImageSize,
  564. IMAGE_FORMAT_RGB888,
  565. targetLowResWidth,
  566. targetLowResHeight,
  567. eConFmtType );
  568. if ( !bRetVal )
  569. {
  570. goto cleanUp;
  571. }
  572. // boil to a single linear color
  573. Vector linearColor;
  574. linearColor.x = linearColor.y = linearColor.z = 0;
  575. for ( int j = 0; j < targetLowResWidth * targetLowResHeight; j++ )
  576. {
  577. linearColor.x += SrgbGammaToLinear( pTargetLowResImage[j*3+0] * 1.0f/255.0f );
  578. linearColor.y += SrgbGammaToLinear( pTargetLowResImage[j*3+1] * 1.0f/255.0f );
  579. linearColor.z += SrgbGammaToLinear( pTargetLowResImage[j*3+2] * 1.0f/255.0f );
  580. }
  581. VectorScale( linearColor, 1.0f/(targetLowResWidth * targetLowResHeight), linearColor );
  582. // serialize as a single texel
  583. targetLowResSample[0] = 255.0f * SrgbLinearToGamma( linearColor[0] );
  584. targetLowResSample[1] = 255.0f * SrgbLinearToGamma( linearColor[1] );
  585. targetLowResSample[2] = 255.0f * SrgbLinearToGamma( linearColor[2] );
  586. // identifies color presence
  587. targetLowResSample[3] = 0xFF;
  588. }
  589. else
  590. {
  591. targetLowResSample[0] = 0;
  592. targetLowResSample[1] = 0;
  593. targetLowResSample[2] = 0;
  594. targetLowResSample[3] = 0;
  595. }
  596. // serialize resource data
  597. for ( int i=0; i<targetResources.Count(); i++ )
  598. {
  599. int resourceDataLength = targetResources[i].m_DataLength;
  600. if ( resourceDataLength == 4 )
  601. {
  602. // data goes directly into structure, as is
  603. targetResources[i].m_EntryInfo.eType |= RSRCF_HAS_NO_DATA_CHUNK;
  604. V_memcpy( &targetResources[i].m_EntryInfo.resData, targetResources[i].m_pData, 4 );
  605. }
  606. else if ( resourceDataLength != 0 )
  607. {
  608. targetResources[i].m_EntryInfo.resData = targetBuf.TellPut();
  609. int swappedLength;
  610. byteSwapWriter.SwapBufferToTargetEndian( &swappedLength, &resourceDataLength );
  611. targetBuf.PutInt( swappedLength );
  612. if ( !targetBuf.IsValid() )
  613. {
  614. goto cleanUp;
  615. }
  616. // put the data
  617. targetBuf.Put( targetResources[i].m_pData, resourceDataLength );
  618. if ( !targetBuf.IsValid() )
  619. {
  620. goto cleanUp;
  621. }
  622. }
  623. }
  624. // mark end of preload data
  625. // preload data might be updated and pushed to extend into the image data mip chain
  626. preloadDataSize = targetBuf.TellPut();
  627. // image starts on an aligned boundary
  628. AlignBuffer( targetBuf, 4 );
  629. // start of image data
  630. targetImageDataOffset = targetBuf.TellPut();
  631. if ( targetImageDataOffset >= 65536 )
  632. {
  633. // possible bug, or may have to offset to 32 bits
  634. Msg( "ConvertVTFToConsoleFormatHelper: non-image portion exceeds 16 bit boundary %s\n", pDebugName );
  635. goto cleanUp;
  636. }
  637. // format conversion, data is stored by ascending mips, 1x1 up to NxN
  638. // data is stored ascending to allow picmipped loads
  639. for ( mip = targetMipCount - 1; mip >= 0; mip-- )
  640. {
  641. for ( frame = 0; frame < targetFrameCount; frame++ )
  642. {
  643. for ( face = 0; face < targetFaceCount; face++ )
  644. {
  645. if ( !SerializeImageData( pSourceVTF, frame, face, mip, targetFormat, targetBuf, eConFmtType ) )
  646. {
  647. goto cleanUp;
  648. }
  649. }
  650. }
  651. }
  652. if ( preloadDataSize < VTFFileHeaderSize( TargetHelper::VER_MAJOR, TargetHelper::VER_MINOR ) )
  653. {
  654. // preload size must be at least what game attempts to initially read
  655. preloadDataSize = VTFFileHeaderSize( TargetHelper::VER_MAJOR, TargetHelper::VER_MINOR );
  656. }
  657. if ( targetBuf.TellPut() <= PRELOAD_VTF_THRESHOLD )
  658. {
  659. // the entire file is too small, preload entirely
  660. preloadDataSize = targetBuf.TellPut();
  661. }
  662. if ( preloadDataSize >= 65536 )
  663. {
  664. // possible overflow due to large frames, faces, and format, may have to offset to 32 bits
  665. Msg( "ConvertVTFToConsoleFormatHelper: preload portion exceeds 16 bit boundary %s\n", pDebugName );
  666. goto cleanUp;
  667. }
  668. // finalize header
  669. V_memset( &targetHeader, 0, sizeof( targetHeader ) );
  670. V_memcpy( targetHeader.fileTypeString, TargetHelper::GetHeaderType(), 4 );
  671. targetHeader.version[0] = TargetHelper::VER_MAJOR;
  672. targetHeader.version[1] = TargetHelper::VER_MINOR;
  673. targetHeader.headerSize = sizeof( targetHeader ) + targetResources.Count() * sizeof( ResourceEntryInfo );
  674. targetHeader.flags = targetFlags;
  675. targetHeader.width = targetWidth;
  676. targetHeader.height = targetHeight;
  677. targetHeader.depth = 1;
  678. targetHeader.numFrames = targetFrameCount;
  679. targetHeader.preloadDataSize = preloadDataSize;
  680. targetHeader.mipSkipCount = mipSkipCount;
  681. targetHeader.numResources = targetResources.Count();
  682. VectorCopy( pSourceVTF->Reflectivity(), targetHeader.reflectivity );
  683. targetHeader.bumpScale = pSourceVTF->BumpScale();
  684. targetHeader.imageFormat = targetFormat;
  685. targetHeader.lowResImageSample[0] = targetLowResSample[0];
  686. targetHeader.lowResImageSample[1] = targetLowResSample[1];
  687. targetHeader.lowResImageSample[2] = targetLowResSample[2];
  688. targetHeader.lowResImageSample[3] = targetLowResSample[3];
  689. if ( !IsGameConsole() )
  690. {
  691. byteSwapWriter.SwapFieldsToTargetEndian( &targetHeader );
  692. }
  693. // write out finalized header
  694. targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  695. targetBuf.Put( &targetHeader, sizeof( targetHeader ) );
  696. if ( !targetBuf.IsValid() )
  697. {
  698. goto cleanUp;
  699. }
  700. // fixup and write out finalized resource dictionary
  701. for ( int i=0; i<targetResources.Count(); i++ )
  702. {
  703. switch ( targetResources[i].m_EntryInfo.eType & ~RSRCF_MASK )
  704. {
  705. case VTF_LEGACY_RSRC_IMAGE:
  706. targetResources[i].m_EntryInfo.resData = targetImageDataOffset;
  707. break;
  708. }
  709. if ( !( targetResources[i].m_EntryInfo.eType & RSRCF_HAS_NO_DATA_CHUNK ) )
  710. {
  711. // swap the offset holders only
  712. byteSwapWriter.SwapBufferToTargetEndian( &targetResources[i].m_EntryInfo.resData );
  713. }
  714. targetBuf.Put( &targetResources[i].m_EntryInfo, sizeof( ResourceEntryInfo ) );
  715. if ( !targetBuf.IsValid() )
  716. {
  717. goto cleanUp;
  718. }
  719. }
  720. targetBuf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
  721. if ( preloadDataSize < targetBuf.TellPut() && pCompressFunc )
  722. {
  723. // only compress files that are not entirely in preload
  724. CUtlBuffer compressedBuffer;
  725. targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, targetImageDataOffset );
  726. bool bCompressed = pCompressFunc( targetBuf, compressedBuffer );
  727. if ( bCompressed )
  728. {
  729. // copy all the header data off
  730. CUtlBuffer headerBuffer;
  731. headerBuffer.EnsureCapacity( targetImageDataOffset );
  732. headerBuffer.Put( targetBuf.Base(), targetImageDataOffset );
  733. // reform the target with the header and then the compressed data
  734. targetBuf.Clear();
  735. targetBuf.Put( headerBuffer.Base(), targetImageDataOffset );
  736. targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
  737. TargetHeaderType *pHeader = (TargetHeaderType *)targetBuf.Base();
  738. if ( !IsGameConsole() )
  739. {
  740. // swap it back into pc space
  741. byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
  742. }
  743. pHeader->compressedSize = compressedBuffer.TellPut();
  744. if ( !IsGameConsole() )
  745. {
  746. // swap it back into console space
  747. byteSwapWriter.SwapFieldsToTargetEndian( pHeader );
  748. }
  749. }
  750. targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  751. }
  752. // success
  753. bRetVal = true;
  754. cleanUp:
  755. if ( pSourceVTF )
  756. {
  757. DestroyVTFTexture( pSourceVTF );
  758. }
  759. for ( int i=0; i<targetResources.Count(); i++ )
  760. {
  761. delete [] (char *)targetResources[i].m_pData;
  762. targetResources[i].m_pData = NULL;
  763. }
  764. return bRetVal;
  765. }
  766. bool ConvertVTFTo360Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip )
  767. {
  768. return ConvertVTFToConsoleFormatHelper< VTF_CONSOLE_360 >( pDebugName, sourceBuf, targetBuf, pCompressFunc, nMaxMip );
  769. }
  770. bool ConvertVTFToPS3Format( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc, int nMaxMip )
  771. {
  772. return ConvertVTFToConsoleFormatHelper< VTF_CONSOLE_PS3 >( pDebugName, sourceBuf, targetBuf, pCompressFunc, nMaxMip );
  773. }
  774. //-----------------------------------------------------------------------------
  775. // Copy the 360 preload data into a buffer. Used by tools to request the preload,
  776. // as part of the preload build process. Caller doesn't have to know cracking details.
  777. // Not to be used at gametime.
  778. //-----------------------------------------------------------------------------
  779. bool GetVTFPreload360Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
  780. {
  781. preloadBufferOut.Purge();
  782. fileBufferIn.ActivateByteSwapping( IsPC() );
  783. VTFFileHeaderX360_t header;
  784. fileBufferIn.GetObjects( &header );
  785. if ( V_strnicmp( header.fileTypeString, "VTFX", 4 ) ||
  786. header.version[0] != VTF_X360_MAJOR_VERSION ||
  787. header.version[1] != VTF_X360_MINOR_VERSION )
  788. {
  789. // bad format
  790. return false;
  791. }
  792. preloadBufferOut.EnsureCapacity( header.preloadDataSize );
  793. preloadBufferOut.Put( fileBufferIn.Base(), header.preloadDataSize );
  794. return true;
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Copy the PS3 preload data into a buffer. Used by tools to request the preload,
  798. // as part of the preload build process. Caller doesn't have to know cracking details.
  799. // Not to be used at gametime.
  800. //-----------------------------------------------------------------------------
  801. bool GetVTFPreloadPS3Data( const char *pDebugName, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
  802. {
  803. preloadBufferOut.Purge();
  804. fileBufferIn.ActivateByteSwapping( IsPC() );
  805. VTFFileHeaderPS3_t header;
  806. fileBufferIn.GetObjects( &header );
  807. if ( V_strnicmp( header.fileTypeString, "VTF3", 4 ) ||
  808. header.version[0] != VTF_PS3_MAJOR_VERSION ||
  809. header.version[1] != VTF_PS3_MINOR_VERSION )
  810. {
  811. // bad format
  812. return false;
  813. }
  814. preloadBufferOut.EnsureCapacity( header.preloadDataSize );
  815. preloadBufferOut.Put( fileBufferIn.Base(), header.preloadDataSize );
  816. return true;
  817. }