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.

405 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // LZMA Codec interface for engine.
  4. //
  5. // LZMA SDK 9.38 beta
  6. // 2015-01-03 : Igor Pavlov : Public domain
  7. // http://www.7-zip.org/
  8. //
  9. //========================================================================//
  10. #define _LZMADECODER_CPP
  11. #include "tier0/platform.h"
  12. #include "tier0/basetypes.h"
  13. #include "tier0/dbg.h"
  14. #include "../utils/lzma/C/7zTypes.h"
  15. #include "../utils/lzma/C/LzmaEnc.h"
  16. #include "../utils/lzma/C/LzmaDec.h"
  17. // Ugly define to let us forward declare the anonymous-struct-typedef that is CLzmaDec in the header.
  18. #define CLzmaDec_t CLzmaDec
  19. #include "tier1/lzmaDecoder.h"
  20. #include "tier1/convar.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. #ifdef OSX
  24. // OS X is having fragmentation issues, and I suspect this 16meg buffer being recreated many times during load is
  25. // hitting a bad case in the default allocator. So this is an experiment to see if it reduces crash rates there.
  26. #define LZMA_DEFAULT_PERSISTENT_BUFFER "1"
  27. #else
  28. #define LZMA_DEFAULT_PERSISTENT_BUFFER "0"
  29. #endif
  30. ConVar lzma_persistent_buffer( "lzma_persistent_buffer", LZMA_DEFAULT_PERSISTENT_BUFFER, FCVAR_NONE,
  31. "If set, attempt to keep a persistent buffer for the LZMA decoder dictionary. " \
  32. "This avoids re-allocating a ~16-64meg buffer for each operation, " \
  33. "at the expensive of keeping extra memory around when it is not in-use." );
  34. // Allocator to pass to LZMA functions
  35. static void *g_pStaticLZMABuf = NULL;
  36. static size_t g_unStaticLZMABufSize = 0;
  37. static uint32 g_unStaticLZMABufRef = 0;
  38. static void *SzAlloc(void *p, size_t size) {
  39. // Don't touch static buffer on other threads.
  40. if ( ThreadInMainThread() )
  41. {
  42. // If nobody is using the persistent buffer and size is above a threshold, use it.
  43. bool bPersistentBuf = (g_pStaticLZMABuf || lzma_persistent_buffer.GetBool()) && size >= (1024 * 1024 * 8) && g_unStaticLZMABufRef == 0;
  44. if ( bPersistentBuf )
  45. {
  46. if ( g_unStaticLZMABufSize < size )
  47. {
  48. g_pStaticLZMABuf = g_pStaticLZMABuf ? realloc( g_pStaticLZMABuf, size ) : malloc( size );
  49. g_unStaticLZMABufSize = size;
  50. }
  51. g_unStaticLZMABufRef++;
  52. return g_pStaticLZMABuf;
  53. }
  54. }
  55. // Not using the persistent buffer
  56. return malloc(size);
  57. }
  58. static void SzFree(void *p, void *address) {
  59. // Don't touch static buffer on other threads.
  60. if ( ThreadInMainThread() )
  61. {
  62. if ( address != NULL && g_unStaticLZMABufRef && address == g_pStaticLZMABuf )
  63. {
  64. g_unStaticLZMABufRef--;
  65. // If the convar was turned off, free the buffer
  66. if ( g_pStaticLZMABuf && g_unStaticLZMABufRef == 0 && !lzma_persistent_buffer.GetBool() )
  67. {
  68. free( g_pStaticLZMABuf );
  69. g_pStaticLZMABuf = NULL;
  70. g_unStaticLZMABufSize = 0;
  71. }
  72. return;
  73. }
  74. }
  75. // Not the static buffer
  76. free(address);
  77. }
  78. static ISzAlloc g_Alloc = { SzAlloc, SzFree };
  79. //-----------------------------------------------------------------------------
  80. // Returns true if buffer is compressed.
  81. //-----------------------------------------------------------------------------
  82. /* static */
  83. bool CLZMA::IsCompressed( unsigned char *pInput )
  84. {
  85. lzma_header_t *pHeader = (lzma_header_t *)pInput;
  86. if ( pHeader && pHeader->id == LZMA_ID )
  87. {
  88. return true;
  89. }
  90. // unrecognized
  91. return false;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Returns uncompressed size of compressed input buffer. Used for allocating output
  95. // buffer for decompression. Returns 0 if input buffer is not compressed.
  96. //-----------------------------------------------------------------------------
  97. /* static */
  98. unsigned int CLZMA::GetActualSize( unsigned char *pInput )
  99. {
  100. lzma_header_t *pHeader = (lzma_header_t *)pInput;
  101. if ( pHeader && pHeader->id == LZMA_ID )
  102. {
  103. return LittleLong( pHeader->actualSize );
  104. }
  105. // unrecognized
  106. return 0;
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Uncompress a buffer, Returns the uncompressed size. Caller must provide an
  110. // adequate sized output buffer or memory corruption will occur.
  111. //-----------------------------------------------------------------------------
  112. /* static */
  113. unsigned int CLZMA::Uncompress( unsigned char *pInput, unsigned char *pOutput )
  114. {
  115. lzma_header_t *pHeader = (lzma_header_t *)pInput;
  116. if ( pHeader->id != LZMA_ID )
  117. {
  118. // not ours
  119. return false;
  120. }
  121. CLzmaDec state;
  122. LzmaDec_Construct(&state);
  123. if ( LzmaDec_Allocate(&state, pHeader->properties, LZMA_PROPS_SIZE, &g_Alloc) != SZ_OK )
  124. {
  125. Assert( false );
  126. return 0;
  127. }
  128. // These are in/out variables
  129. SizeT outProcessed = pHeader->actualSize;
  130. SizeT inProcessed = pHeader->lzmaSize;
  131. ELzmaStatus status;
  132. SRes result = LzmaDecode( (Byte *)pOutput, &outProcessed, (Byte *)(pInput + sizeof( lzma_header_t ) ),
  133. &inProcessed, (Byte *)pHeader->properties, LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &g_Alloc );
  134. LzmaDec_Free(&state, &g_Alloc);
  135. if ( result != SZ_OK || pHeader->actualSize != outProcessed )
  136. {
  137. Warning( "LZMA Decompression failed (%i)\n", result );
  138. return 0;
  139. }
  140. return outProcessed;
  141. }
  142. CLZMAStream::CLZMAStream()
  143. : m_pDecoderState( NULL ),
  144. m_nActualSize( 0 ),
  145. m_nActualBytesRead ( 0 ),
  146. m_nCompressedSize( 0 ),
  147. m_nCompressedBytesRead ( 0 ),
  148. m_bParsedHeader( false ),
  149. m_bZIPStyleHeader( false )
  150. {}
  151. CLZMAStream::~CLZMAStream()
  152. {
  153. FreeDecoderState();
  154. }
  155. void CLZMAStream::FreeDecoderState()
  156. {
  157. if ( m_pDecoderState )
  158. {
  159. LzmaDec_Free( m_pDecoderState, &g_Alloc );
  160. delete m_pDecoderState;
  161. m_pDecoderState = NULL;
  162. }
  163. }
  164. bool CLZMAStream::CreateDecoderState( const unsigned char *pProperties )
  165. {
  166. CLzmaDec *pDecoderState = new CLzmaDec();
  167. LzmaDec_Construct( pDecoderState );
  168. if ( LzmaDec_Allocate( pDecoderState, pProperties, LZMA_PROPS_SIZE, &g_Alloc) != SZ_OK )
  169. {
  170. AssertMsg( false, "Failed to allocate lzma decoder state" );
  171. delete pDecoderState;
  172. return false;
  173. }
  174. LzmaDec_Init( pDecoderState );
  175. // Replace current state
  176. Assert( !m_pDecoderState );
  177. FreeDecoderState();
  178. m_pDecoderState = pDecoderState;
  179. return true;
  180. }
  181. // Attempt to read up to nMaxInputBytes from the compressed stream, writing up to nMaxOutputBytes to pOutput.
  182. // Returns false if read stops due to an error.
  183. bool CLZMAStream::Read( unsigned char *pInput, unsigned int nMaxInputBytes,
  184. unsigned char *pOutput, unsigned int nMaxOutputBytes,
  185. /* out */ unsigned int &nCompressedBytesRead,
  186. /* out */ unsigned int &nOutputBytesWritten )
  187. {
  188. nCompressedBytesRead = 0;
  189. nOutputBytesWritten = 0;
  190. bool bStartedWithHeader = m_bParsedHeader;
  191. // Check for initial chunk of data
  192. if ( !m_bParsedHeader )
  193. {
  194. unsigned int nBytesConsumed = 0;
  195. eHeaderParse parseResult = TryParseHeader( pInput, nMaxInputBytes, nBytesConsumed );
  196. if ( parseResult == eHeaderParse_NeedMoreBytes )
  197. {
  198. // Not an error, just need more data to continue
  199. return true;
  200. }
  201. else if ( parseResult != eHeaderParse_OK )
  202. {
  203. Assert( parseResult == eHeaderParse_Fail );
  204. // Invalid header
  205. return false;
  206. }
  207. // Header consumed, fall through to continue read after it
  208. nCompressedBytesRead += nBytesConsumed;
  209. pInput += nBytesConsumed;
  210. nMaxInputBytes -= nBytesConsumed;
  211. }
  212. // These are input ( available size ) *and* output ( size processed ) vars for lzma
  213. SizeT expectedInputRemaining = m_nCompressedSize - Min( m_nCompressedBytesRead + nCompressedBytesRead, m_nCompressedSize );
  214. SizeT expectedOutputRemaining = m_nActualSize - m_nActualBytesRead;
  215. SizeT inSize = Min( (SizeT)nMaxInputBytes, expectedInputRemaining );
  216. SizeT outSize = Min( (SizeT)nMaxOutputBytes, expectedOutputRemaining );
  217. ELzmaStatus status;
  218. ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
  219. if ( inSize == expectedInputRemaining && outSize == expectedOutputRemaining )
  220. {
  221. // Expect to finish decoding this call.
  222. finishMode = LZMA_FINISH_END;
  223. }
  224. SRes result = LzmaDec_DecodeToBuf( m_pDecoderState, pOutput, &outSize,
  225. pInput, &inSize, finishMode, &status );
  226. // DevMsg("[%p] Running lzmaDecode:\n"
  227. // " pInput: %p\n"
  228. // " nMaxInputBytes: %i\n"
  229. // " pOutput: %p\n"
  230. // " nMaxOutputBytes: %u\n"
  231. // " inSize: %u\n"
  232. // " outSize: %u\n"
  233. // " result: %u\n"
  234. // " status: %i\n"
  235. // " m_nActualSize: %u\n"
  236. // " m_nActualBytesRead: %u\n",
  237. // this, pInput, nMaxInputBytes, pOutput, nMaxOutputBytes,
  238. // inSize, outSize, result, status, m_nActualSize, m_nActualBytesRead);
  239. if ( result != SZ_OK )
  240. {
  241. if ( !bStartedWithHeader )
  242. {
  243. // If we're returning false, we need to pretend we didn't consume anything.
  244. FreeDecoderState();
  245. m_bParsedHeader = false;
  246. }
  247. return false;
  248. }
  249. nCompressedBytesRead += inSize;
  250. nOutputBytesWritten += outSize;
  251. m_nCompressedBytesRead += nCompressedBytesRead;
  252. m_nActualBytesRead += nOutputBytesWritten;
  253. Assert( m_nCompressedBytesRead <= m_nCompressedSize );
  254. return true;
  255. }
  256. bool CLZMAStream::GetExpectedBytesRemaining( /* out */ unsigned int &nBytesRemaining )
  257. {
  258. if ( !m_bParsedHeader && !m_bZIPStyleHeader ) {
  259. return false;
  260. }
  261. nBytesRemaining = m_nActualSize - m_nActualBytesRead;
  262. return true;
  263. }
  264. void CLZMAStream::InitZIPHeader( unsigned int nCompressedSize, unsigned int nOriginalSize )
  265. {
  266. if ( m_bParsedHeader || m_bZIPStyleHeader )
  267. {
  268. AssertMsg( !m_bParsedHeader && !m_bZIPStyleHeader,
  269. "LZMA Stream: InitZIPHeader() called on stream past header" );
  270. return;
  271. }
  272. m_nCompressedSize = nCompressedSize;
  273. m_nActualSize = nOriginalSize;
  274. // Signal to TryParseHeader to expect a zip-style header (which wont have the size values)
  275. m_bZIPStyleHeader = true;
  276. }
  277. CLZMAStream::eHeaderParse CLZMAStream::TryParseHeader( unsigned char *pInput, unsigned int nBytesAvailable, /* out */ unsigned int &nBytesConsumed )
  278. {
  279. nBytesConsumed = 0;
  280. if ( m_bParsedHeader )
  281. {
  282. AssertMsg( !m_bParsedHeader, "CLZMAStream::ReadSourceHeader called on already initialized stream" );
  283. return eHeaderParse_Fail;
  284. }
  285. if ( m_bZIPStyleHeader )
  286. {
  287. // ZIP Spec, 5.8.8
  288. // LZMA Version Information 2 bytes
  289. // LZMA Properties Size 2 bytes
  290. // LZMA Properties Data variable, defined by "LZMA Properties Size"
  291. if ( nBytesAvailable < 4 )
  292. {
  293. // No error, but need more input to continue
  294. return eHeaderParse_NeedMoreBytes;
  295. }
  296. // Should probably check this
  297. // unsigned char nLZMAVer[2] = { pInput[0], pInput[1] };
  298. uint16 nLZMAPropertiesSize = LittleWord( *(uint16 *)(pInput + 2) );
  299. nBytesConsumed += 4;
  300. if ( nLZMAPropertiesSize != LZMA_PROPS_SIZE )
  301. {
  302. Warning( "LZMA stream: Unexpected LZMA properties size: %hu, expecting %u. Version mismatch?\n",
  303. nLZMAPropertiesSize, LZMA_PROPS_SIZE );
  304. return eHeaderParse_Fail;
  305. }
  306. if ( nBytesAvailable < static_cast<unsigned int>(nLZMAPropertiesSize) + 4 )
  307. {
  308. return eHeaderParse_NeedMoreBytes;
  309. }
  310. // Looks reasonable, try to parse
  311. if ( !CreateDecoderState( (Byte *)pInput + 4 ) )
  312. {
  313. AssertMsg( false, "Failed decoding Lzma properties" );
  314. return eHeaderParse_Fail;
  315. }
  316. nBytesConsumed += nLZMAPropertiesSize;
  317. }
  318. else
  319. {
  320. // Else native source engine style header
  321. if ( nBytesAvailable < sizeof( lzma_header_t ) )
  322. {
  323. // need more input to continue
  324. return eHeaderParse_NeedMoreBytes;
  325. }
  326. m_nActualSize = CLZMA::GetActualSize( pInput );
  327. if ( !m_nActualSize )
  328. {
  329. // unrecognized
  330. Warning( "Unrecognized LZMA data\n" );
  331. return eHeaderParse_Fail;
  332. }
  333. if ( !CreateDecoderState( ((lzma_header_t *)pInput)->properties ) )
  334. {
  335. AssertMsg( false, "Failed decoding Lzma properties" );
  336. return eHeaderParse_Fail;
  337. }
  338. m_nCompressedSize = LittleLong( ((lzma_header_t *)pInput)->lzmaSize ) + sizeof( lzma_header_t );
  339. nBytesConsumed += sizeof( lzma_header_t );
  340. }
  341. m_bParsedHeader = true;
  342. return eHeaderParse_OK;
  343. }