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.

348 lines
9.8 KiB

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