Team Fortress 2 Source Code as on 22/4/2020
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.

891 lines
28 KiB

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