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.

332 lines
7.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // LZMA Codec interface for engine. Based largely on LzmaUtil.c in SDK
  4. //
  5. // LZMA SDK 9.38 beta
  6. // 2015-01-03 : Igor Pavlov : Public domain
  7. // http://www.7-zip.org/
  8. //
  9. //========================================================================//
  10. #ifdef POSIX
  11. #include <stdlib.h>
  12. #endif
  13. #include "tier0/memdbgon.h"
  14. #include "../../public/tier1/lzmaDecoder.h"
  15. #include "C/7zTypes.h"
  16. #include "C/LzmaEnc.h"
  17. #include "C/LzmaDec.h"
  18. #include "tier0/dbg.h"
  19. // Allocator to pass to LZMA functions
  20. static void *SzAlloc(void *p, size_t size) { return malloc(size); }
  21. static void SzFree(void *p, void *address) { free(address); }
  22. static ISzAlloc g_Alloc = { SzAlloc, SzFree };
  23. // lzma buffers will have a 13 byte trivial header
  24. // [0] reserved
  25. // [1..4] dictionary size, little endian
  26. // [5..8] uncompressed size, little endian low word
  27. // [9..12] uncompressed size, little endian high word
  28. // [13..] lzma compressed data
  29. #define LZMA_ORIGINAL_HEADER_SIZE 13
  30. SRes CInStreamRam_StaticRead(void *p, void *buf, size_t *size );
  31. size_t COutStreamRam_StaticWrite(void *p, const void *buf, size_t size);
  32. class CInStreamRam : public ISeqInStream
  33. {
  34. const Byte *Data;
  35. size_t Size;
  36. size_t Pos;
  37. public:
  38. void Init(const Byte *data, size_t size)
  39. {
  40. Data = data;
  41. Size = size;
  42. Pos = 0;
  43. Read = CInStreamRam_StaticRead;
  44. }
  45. SRes DoRead( void *buf, size_t *size )
  46. {
  47. size_t inSize = *size;
  48. UInt32 remain = Size - Pos;
  49. if (inSize > remain)
  50. inSize = remain;
  51. for (UInt32 i = 0; i < inSize; i++)
  52. ((Byte *)buf)[i] = Data[Pos + i];
  53. Pos += inSize;
  54. *size = inSize;
  55. return SZ_OK;
  56. }
  57. };
  58. class COutStreamRam: public ISeqOutStream
  59. {
  60. size_t Size;
  61. public:
  62. Byte *Data;
  63. size_t Pos;
  64. bool Overflow;
  65. void Init(Byte *data, size_t size)
  66. {
  67. Data = data;
  68. Size = size;
  69. Pos = 0;
  70. Overflow = false;
  71. Write = COutStreamRam_StaticWrite;
  72. }
  73. size_t DoWrite( const void *buf, size_t size )
  74. {
  75. UInt32 i;
  76. for (i = 0; i < size && Pos < Size; i++)
  77. Data[Pos++] = ((const Byte *)buf)[i];
  78. if (i != size)
  79. {
  80. Overflow = true;
  81. }
  82. return i;
  83. }
  84. };
  85. SRes CInStreamRam_StaticRead(void *p, void *buf, size_t *size )
  86. {
  87. return reinterpret_cast<CInStreamRam *>(p)->DoRead( buf, size );
  88. }
  89. size_t COutStreamRam_StaticWrite(void *p, const void *buf, size_t size)
  90. {
  91. return reinterpret_cast<COutStreamRam *>(p)->DoWrite( buf, size );
  92. }
  93. SRes
  94. LzmaEncode( const Byte *inBuffer,
  95. size_t inSize,
  96. Byte *outBuffer,
  97. size_t outSize,
  98. size_t *outSizeProcessed )
  99. {
  100. // Based on Encode helper in SDK/LzmaUtil
  101. *outSizeProcessed = 0;
  102. const size_t kMinDestSize = LZMA_ORIGINAL_HEADER_SIZE;
  103. if ( outSize < kMinDestSize )
  104. {
  105. return SZ_ERROR_FAIL;
  106. }
  107. CLzmaEncHandle enc;
  108. SRes res;
  109. CLzmaEncProps props;
  110. enc = LzmaEnc_Create( &g_Alloc );
  111. if ( !enc )
  112. {
  113. return SZ_ERROR_FAIL;
  114. }
  115. LzmaEncProps_Init( &props );
  116. res = LzmaEnc_SetProps( enc, &props );
  117. if ( res != SZ_OK )
  118. {
  119. return res;
  120. }
  121. COutStreamRam outStream;
  122. outStream.Init( outBuffer, outSize );
  123. Byte header[LZMA_PROPS_SIZE + 8];
  124. size_t headerSize = LZMA_PROPS_SIZE;
  125. int i;
  126. res = LzmaEnc_WriteProperties( enc, header, &headerSize );
  127. if ( res != SZ_OK )
  128. {
  129. return res;
  130. }
  131. // Uncompressed size after properties in header
  132. for (i = 0; i < 8; i++)
  133. {
  134. header[headerSize++] = (Byte)(inSize >> (8 * i));
  135. }
  136. if ( outStream.DoWrite( header, headerSize ) != headerSize )
  137. {
  138. res = SZ_ERROR_WRITE;
  139. }
  140. else if ( res == SZ_OK )
  141. {
  142. CInStreamRam inStream;
  143. inStream.Init( inBuffer, inSize );
  144. res = LzmaEnc_Encode( enc, &outStream, &inStream, NULL, &g_Alloc, &g_Alloc );
  145. if ( outStream.Overflow )
  146. {
  147. res = SZ_ERROR_FAIL;
  148. }
  149. else
  150. {
  151. *outSizeProcessed = outStream.Pos;
  152. }
  153. }
  154. LzmaEnc_Destroy( enc, &g_Alloc, &g_Alloc );
  155. return res;
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Encoding glue. Returns non-null Compressed buffer if successful.
  159. // Caller must free.
  160. //-----------------------------------------------------------------------------
  161. unsigned char *LZMA_Compress( unsigned char *pInput,
  162. unsigned int inputSize,
  163. unsigned int *pOutputSize )
  164. {
  165. *pOutputSize = 0;
  166. // using same work buffer calcs as the SDK 105% + 64K
  167. unsigned outSize = inputSize/20 * 21 + (1<<16);
  168. unsigned char *pOutputBuffer = (unsigned char*)malloc( outSize );
  169. if ( !pOutputBuffer )
  170. {
  171. return NULL;
  172. }
  173. // compress, skipping past our header
  174. size_t compressedSize;
  175. int result = LzmaEncode( pInput, inputSize, pOutputBuffer + sizeof( lzma_header_t ), outSize - sizeof( lzma_header_t ), &compressedSize );
  176. if ( result != SZ_OK )
  177. {
  178. Warning( "LZMA encode failed (%i)\n", result );
  179. Assert( result == SZ_OK );
  180. free( pOutputBuffer );
  181. return NULL;
  182. }
  183. // construct our header, strip theirs
  184. lzma_header_t *pHeader = (lzma_header_t *)pOutputBuffer;
  185. pHeader->id = LZMA_ID;
  186. pHeader->actualSize = inputSize;
  187. pHeader->lzmaSize = compressedSize - LZMA_ORIGINAL_HEADER_SIZE;
  188. memcpy( pHeader->properties, pOutputBuffer + sizeof( lzma_header_t ), LZMA_PROPS_SIZE );
  189. // shift the compressed data into place
  190. memmove( pOutputBuffer + sizeof( lzma_header_t ),
  191. pOutputBuffer + sizeof( lzma_header_t ) + LZMA_ORIGINAL_HEADER_SIZE,
  192. compressedSize - LZMA_ORIGINAL_HEADER_SIZE );
  193. // final output size is our header plus compressed bits
  194. *pOutputSize = sizeof( lzma_header_t ) + compressedSize - LZMA_ORIGINAL_HEADER_SIZE;
  195. return pOutputBuffer;
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Above, but returns null if compression would not yield a size improvement
  199. //-----------------------------------------------------------------------------
  200. unsigned char *LZMA_OpportunisticCompress( unsigned char *pInput,
  201. unsigned int inputSize,
  202. unsigned int *pOutputSize )
  203. {
  204. unsigned char *pRet = LZMA_Compress( pInput, inputSize, pOutputSize );
  205. if ( *pOutputSize <= inputSize )
  206. {
  207. // compression got worse or stayed the same
  208. free( pRet );
  209. return NULL;
  210. }
  211. return pRet;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Decoding glue. Returns TRUE if succesful.
  215. //-----------------------------------------------------------------------------
  216. bool LZMA_Uncompress( unsigned char *pInBuffer,
  217. unsigned char **ppOutBuffer,
  218. unsigned int *pOutSize )
  219. {
  220. *ppOutBuffer = NULL;
  221. *pOutSize = 0;
  222. lzma_header_t *pHeader = (lzma_header_t *)pInBuffer;
  223. if ( pHeader->id != LZMA_ID )
  224. {
  225. // not ours
  226. return false;
  227. }
  228. CLzmaDec state;
  229. LzmaDec_Construct(&state);
  230. if ( LzmaDec_Allocate(&state, pHeader->properties, LZMA_PROPS_SIZE, &g_Alloc) != SZ_OK )
  231. {
  232. return false;
  233. }
  234. unsigned char *pOutBuffer = (unsigned char *)malloc( pHeader->actualSize );
  235. if ( !pOutBuffer )
  236. {
  237. LzmaDec_Free(&state, &g_Alloc);
  238. return false;
  239. }
  240. // These are in/out variables
  241. SizeT outProcessed = pHeader->actualSize;
  242. SizeT inProcessed = pHeader->lzmaSize;
  243. ELzmaStatus status;
  244. SRes result = LzmaDecode( (Byte *)pOutBuffer, &outProcessed, (Byte *)(pInBuffer + sizeof( lzma_header_t ) ),
  245. &inProcessed, (Byte *)pHeader->properties, LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &g_Alloc );
  246. LzmaDec_Free(&state, &g_Alloc);
  247. if ( result != SZ_OK || pHeader->actualSize != outProcessed )
  248. {
  249. free( pOutBuffer );
  250. return false;
  251. }
  252. *ppOutBuffer = pOutBuffer;
  253. *pOutSize = pHeader->actualSize;
  254. return true;
  255. }
  256. bool LZMA_IsCompressed( unsigned char *pInput )
  257. {
  258. lzma_header_t *pHeader = (lzma_header_t *)pInput;
  259. if ( pHeader && pHeader->id == LZMA_ID )
  260. {
  261. return true;
  262. }
  263. // unrecognized
  264. return false;
  265. }
  266. unsigned int LZMA_GetActualSize( unsigned char *pInput )
  267. {
  268. lzma_header_t *pHeader = (lzma_header_t *)pInput;
  269. if ( pHeader && pHeader->id == LZMA_ID )
  270. {
  271. return pHeader->actualSize;
  272. }
  273. // unrecognized
  274. return 0;
  275. }