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.

347 lines
9.7 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The 360 VTF file format I/O class to help simplify access to 360 VTF files.
  4. // 360 Formatted VTF's are stored ascending 1x1 up to NxN. Disk format and unserialized
  5. // formats are expected to be the same.
  6. //
  7. //=====================================================================================//
  8. #include "bitmap/imageformat.h"
  9. #include "cvtf.h"
  10. #include "utlbuffer.h"
  11. #include "tier0/dbg.h"
  12. #include "tier0/mem.h"
  13. #include "tier2/fileutils.h"
  14. #include "byteswap.h"
  15. #include "filesystem.h"
  16. #include "mathlib/mathlib.h"
  17. #include "tier1/lzmaDecoder.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. //-----------------------------------------------------------------------------
  21. // Callback for UpdateOrCreate utility function - swaps a vtf file.
  22. //-----------------------------------------------------------------------------
  23. static bool VTFCreateCallback( const char *pSourceName, const char *pTargetName, const char *pPathID, void *pExtraData )
  24. {
  25. // Generate the file
  26. CUtlBuffer sourceBuf;
  27. CUtlBuffer targetBuf;
  28. bool bOk = g_pFullFileSystem->ReadFile( pSourceName, pPathID, sourceBuf );
  29. if ( bOk )
  30. {
  31. bOk = ConvertVTFTo360Format( pSourceName, sourceBuf, targetBuf, NULL );
  32. if ( bOk )
  33. {
  34. bOk = g_pFullFileSystem->WriteFile( pTargetName, pPathID, targetBuf );
  35. }
  36. }
  37. if ( !bOk )
  38. {
  39. Warning( "Failed to create %s\n", pTargetName );
  40. }
  41. return bOk;
  42. }
  43. //-----------------------------------------------------------------------------
  44. // Calls utility function to create .360 version of a vtf file.
  45. //-----------------------------------------------------------------------------
  46. int CVTFTexture::UpdateOrCreate( const char *pFilename, const char *pPathID, bool bForce )
  47. {
  48. return ::UpdateOrCreate( pFilename, NULL, 0, pPathID, VTFCreateCallback, bForce, NULL );
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Determine size of file, possibly smaller if skipping top mip levels.
  52. //-----------------------------------------------------------------------------
  53. int CVTFTexture::FileSize( bool bPreloadOnly, int nMipSkipCount ) const
  54. {
  55. if ( bPreloadOnly )
  56. {
  57. // caller wants size of preload
  58. return m_iPreloadDataSize;
  59. }
  60. const ResourceEntryInfo *pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  61. if ( !pEntryInfo )
  62. {
  63. // has to exist
  64. Assert( 0 );
  65. return 0;
  66. }
  67. int iImageDataOffset = pEntryInfo->resData;
  68. if ( m_iCompressedSize )
  69. {
  70. // file is compressed, mip skipping is non-applicable at this stage
  71. return iImageDataOffset + m_iCompressedSize;
  72. }
  73. // caller gets file size, possibly truncated due to mip skipping
  74. int nFaceSize = ComputeFaceSize( nMipSkipCount );
  75. return iImageDataOffset + m_nFrameCount * m_nFaceCount * nFaceSize;
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Unserialization of image data from buffer
  79. //-----------------------------------------------------------------------------
  80. bool CVTFTexture::LoadImageData( CUtlBuffer &buf, bool bBufferIsVolatile, int nMipSkipCount )
  81. {
  82. ResourceEntryInfo *pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  83. if ( !pEntryInfo )
  84. {
  85. // has to exist
  86. Assert( 0 );
  87. return false;
  88. }
  89. int iImageDataOffset = pEntryInfo->resData;
  90. // Fix up the mip count + size based on how many mip levels we skip...
  91. if ( nMipSkipCount > 0 )
  92. {
  93. if ( nMipSkipCount >= m_nMipCount )
  94. {
  95. nMipSkipCount = 0;
  96. }
  97. ComputeMipLevelDimensions( nMipSkipCount, &m_nWidth, &m_nHeight, &m_nDepth );
  98. m_nMipCount -= nMipSkipCount;
  99. m_nMipSkipCount += nMipSkipCount;
  100. }
  101. int iImageSize = ComputeFaceSize();
  102. iImageSize = m_nFrameCount * m_nFaceCount * iImageSize;
  103. // seek to start of image data
  104. // The mip levels are stored on disk ascending from smallest (1x1) to largest (NxN) to allow for picmip truncated reads
  105. buf.SeekGet( CUtlBuffer::SEEK_HEAD, iImageDataOffset );
  106. CLZMA lzma;
  107. if ( m_iCompressedSize )
  108. {
  109. unsigned char *pCompressedData = (unsigned char *)buf.PeekGet();
  110. if ( !lzma.IsCompressed( pCompressedData ) )
  111. {
  112. // huh? header says it was compressed
  113. Assert( 0 );
  114. return false;
  115. }
  116. // have to decode entire image
  117. unsigned int originalSize = lzma.GetActualSize( pCompressedData );
  118. AllocateImageData( originalSize );
  119. unsigned int outputLength = lzma.Uncompress( pCompressedData, m_pImageData );
  120. return ( outputLength == originalSize );
  121. }
  122. bool bOK;
  123. if ( bBufferIsVolatile )
  124. {
  125. AllocateImageData( iImageSize );
  126. buf.Get( m_pImageData, iImageSize );
  127. bOK = buf.IsValid();
  128. }
  129. else
  130. {
  131. // safe to alias
  132. m_pImageData = (unsigned char *)buf.PeekGet( iImageSize, 0 );
  133. bOK = ( m_pImageData != NULL );
  134. }
  135. return bOK;
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Unserialization
  139. //-----------------------------------------------------------------------------
  140. bool CVTFTexture::ReadHeader( CUtlBuffer &buf, VTFFileHeaderX360_t &header )
  141. {
  142. memset( &header, 0, sizeof( VTFFileHeaderX360_t ) );
  143. buf.GetObjects( &header );
  144. if ( !buf.IsValid() )
  145. {
  146. Warning( "*** Error getting header from a X360 VTF file.\n" );
  147. return false;
  148. }
  149. // Validity check
  150. if ( Q_strncmp( header.fileTypeString, "VTFX", 4 ) )
  151. {
  152. Warning( "*** Tried to load a PC VTF file as a X360 VTF file!\n" );
  153. return false;
  154. }
  155. if ( header.version[0] != VTF_X360_MAJOR_VERSION && header.version[1] != VTF_X360_MINOR_VERSION )
  156. {
  157. Warning( "*** Encountered X360 VTF file with an invalid version!\n" );
  158. return false;
  159. }
  160. if ( ( header.flags & TEXTUREFLAGS_ENVMAP ) && ( header.width != header.height ) )
  161. {
  162. Warning( "*** Encountered X360 VTF non-square cubemap!\n" );
  163. return false;
  164. }
  165. if ( ( header.flags & TEXTUREFLAGS_ENVMAP ) && ( header.depth != 1 ) )
  166. {
  167. Warning( "*** Encountered X360 VTF volume texture cubemap!\n" );
  168. return false;
  169. }
  170. if ( header.width <= 0 || header.height <= 0 || header.depth <= 0 )
  171. {
  172. Warning( "*** Encountered X360 VTF invalid texture size!\n" );
  173. return false;
  174. }
  175. return true;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Unserialization. Can optionally alias image components to a non-volatile buffer,
  179. // which prevents unecessary copies. Disk format and memory format of the image
  180. // components are explicitly the same.
  181. //-----------------------------------------------------------------------------
  182. bool CVTFTexture::UnserializeFromBuffer( CUtlBuffer &buf, bool bBufferIsVolatile, bool bHeaderOnly, bool bPreloadOnly, int nMipSkipCount )
  183. {
  184. VTFFileHeaderX360_t header;
  185. ResourceEntryInfo *pEntryInfo;
  186. if ( !ReadHeader( buf, header ) )
  187. {
  188. return false;
  189. }
  190. // must first release any prior owned memory or reset aliases, otherwise corruption if types intermingled
  191. ReleaseImageMemory();
  192. ReleaseResources();
  193. m_nVersion[0] = header.version[0];
  194. m_nVersion[1] = header.version[1];
  195. m_nWidth = header.width;
  196. m_nHeight = header.height;
  197. m_nDepth = header.depth;
  198. m_Format = header.imageFormat;
  199. m_nFlags = header.flags;
  200. m_nFrameCount = header.numFrames;
  201. m_nFaceCount = ( m_nFlags & TEXTUREFLAGS_ENVMAP ) ? CUBEMAP_FACE_COUNT-1 : 1;
  202. m_nMipCount = ComputeMipCount();
  203. m_nMipSkipCount = header.mipSkipCount;
  204. m_vecReflectivity = header.reflectivity;
  205. m_flBumpScale = header.bumpScale;
  206. m_iPreloadDataSize = header.preloadDataSize;
  207. m_iCompressedSize = header.compressedSize;
  208. m_LowResImageFormat = IMAGE_FORMAT_RGB888;
  209. if ( header.lowResImageSample[3] )
  210. {
  211. // nonzero denotes validity of color value
  212. m_nLowResImageWidth = 1;
  213. m_nLowResImageHeight = 1;
  214. *(unsigned int *)m_LowResImageSample = *(unsigned int *)header.lowResImageSample;
  215. }
  216. else
  217. {
  218. m_nLowResImageWidth = 0;
  219. m_nLowResImageHeight = 0;
  220. *(unsigned int *)m_LowResImageSample = 0;
  221. }
  222. // 360 always has the image resource
  223. Assert( header.numResources >= 1 );
  224. m_arrResourcesInfo.SetCount( header.numResources );
  225. m_arrResourcesData.SetCount( header.numResources );
  226. // Read the dictionary of resources info
  227. buf.Get( m_arrResourcesInfo.Base(), m_arrResourcesInfo.Count() * sizeof( ResourceEntryInfo ) );
  228. if ( !buf.IsValid() )
  229. {
  230. return false;
  231. }
  232. pEntryInfo = FindResourceEntryInfo( VTF_LEGACY_RSRC_IMAGE );
  233. if ( !pEntryInfo )
  234. {
  235. // not optional, has to be present
  236. Assert( 0 );
  237. return false;
  238. }
  239. if ( bHeaderOnly )
  240. {
  241. // caller wants header components only
  242. // resource data chunks are NOT unserialized!
  243. return true;
  244. }
  245. if ( !LoadNewResources( buf ) )
  246. {
  247. return false;
  248. }
  249. if ( bPreloadOnly )
  250. {
  251. // caller wants preload portion only, everything up to the image
  252. return true;
  253. }
  254. if ( !LoadImageData( buf, bBufferIsVolatile, nMipSkipCount ) )
  255. {
  256. return false;
  257. }
  258. return true;
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Discard image data to free up memory.
  262. //-----------------------------------------------------------------------------
  263. void CVTFTexture::ReleaseImageMemory()
  264. {
  265. // valid sizes identify locally owned memory
  266. if ( m_nImageAllocSize )
  267. {
  268. delete [] m_pImageData;
  269. m_nImageAllocSize = 0;
  270. }
  271. // block pointers could be owned or aliased, always clear
  272. // ensures other caller's don't free an aliased pointer
  273. m_pImageData = NULL;
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Attributes...
  277. //-----------------------------------------------------------------------------
  278. bool CVTFTexture::IsPreTiled() const
  279. {
  280. return false;
  281. }
  282. int CVTFTexture::MappingWidth() const
  283. {
  284. return m_nWidth << m_nMipSkipCount;
  285. }
  286. int CVTFTexture::MappingHeight() const
  287. {
  288. return m_nHeight << m_nMipSkipCount;
  289. }
  290. int CVTFTexture::MappingDepth() const
  291. {
  292. return m_nDepth << m_nMipSkipCount;
  293. }
  294. int CVTFTexture::MipSkipCount() const
  295. {
  296. return m_nMipSkipCount;
  297. }
  298. unsigned char *CVTFTexture::LowResImageSample()
  299. {
  300. return &m_LowResImageSample[0];
  301. }