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.

429 lines
9.5 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // LZSS Codec. Designed for fast cheap gametime encoding/decoding. Compression results
  4. // are not aggresive as other alogrithms, but gets 2:1 on most arbitrary uncompressed data.
  5. //
  6. //=====================================================================================//
  7. #include "tier0/platform.h"
  8. #include "tier0/dbg.h"
  9. #include "tier0/vprof.h"
  10. #include "tier0/etwprof.h"
  11. #include "tier1/lzss.h"
  12. #include "tier1/utlbuffer.h"
  13. #define LZSS_LOOKSHIFT 4
  14. #define LZSS_LOOKAHEAD ( 1 << LZSS_LOOKSHIFT )
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. //-----------------------------------------------------------------------------
  18. // Returns true if buffer is compressed.
  19. //-----------------------------------------------------------------------------
  20. bool CLZSS::IsCompressed( const unsigned char *pInput )
  21. {
  22. lzss_header_t *pHeader = (lzss_header_t *)pInput;
  23. if ( pHeader && pHeader->id == LZSS_ID )
  24. {
  25. return true;
  26. }
  27. // unrecognized
  28. return false;
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Returns uncompressed size of compressed input buffer. Used for allocating output
  32. // buffer for decompression. Returns 0 if input buffer is not compressed.
  33. //-----------------------------------------------------------------------------
  34. unsigned int CLZSS::GetActualSize( const unsigned char *pInput )
  35. {
  36. lzss_header_t *pHeader = (lzss_header_t *)pInput;
  37. if ( pHeader && pHeader->id == LZSS_ID )
  38. {
  39. return LittleLong( pHeader->actualSize );
  40. }
  41. // unrecognized
  42. return 0;
  43. }
  44. void CLZSS::BuildHash( const unsigned char *pData )
  45. {
  46. lzss_list_t *pList;
  47. lzss_node_t *pTarget;
  48. int targetindex = (unsigned int)pData & ( m_nWindowSize - 1 );
  49. pTarget = &m_pHashTarget[targetindex];
  50. if ( pTarget->pData )
  51. {
  52. pList = &m_pHashTable[*pTarget->pData];
  53. if ( pTarget->pPrev )
  54. {
  55. pList->pEnd = pTarget->pPrev;
  56. pTarget->pPrev->pNext = 0;
  57. }
  58. else
  59. {
  60. pList->pEnd = 0;
  61. pList->pStart = 0;
  62. }
  63. }
  64. pList = &m_pHashTable[*pData];
  65. pTarget->pData = pData;
  66. pTarget->pPrev = 0;
  67. pTarget->pNext = pList->pStart;
  68. if ( pList->pStart )
  69. {
  70. pList->pStart->pPrev = pTarget;
  71. }
  72. else
  73. {
  74. pList->pEnd = pTarget;
  75. }
  76. pList->pStart = pTarget;
  77. }
  78. unsigned char *CLZSS::CompressNoAlloc( const unsigned char *pInput, int inputLength, unsigned char *pOutputBuf, unsigned int *pOutputSize )
  79. {
  80. if ( inputLength <= sizeof( lzss_header_t ) + 8 )
  81. {
  82. return NULL;
  83. }
  84. VPROF( "CLZSS::CompressNoAlloc" );
  85. ETWMark1I("CompressNoAlloc", inputLength );
  86. // create the compression work buffers, small enough (~64K) for stack
  87. m_pHashTable = (lzss_list_t *)stackalloc( 256 * sizeof( lzss_list_t ) );
  88. memset( m_pHashTable, 0, 256 * sizeof( lzss_list_t ) );
  89. m_pHashTarget = (lzss_node_t *)stackalloc( m_nWindowSize * sizeof( lzss_node_t ) );
  90. memset( m_pHashTarget, 0, m_nWindowSize * sizeof( lzss_node_t ) );
  91. // allocate the output buffer, compressed buffer is expected to be less, caller will free
  92. unsigned char *pStart = pOutputBuf;
  93. // prevent compression failure (inflation), leave enough to allow dribble eof bytes
  94. unsigned char *pEnd = pStart + inputLength - sizeof ( lzss_header_t ) - 8;
  95. // set the header
  96. lzss_header_t *pHeader = (lzss_header_t *)pStart;
  97. pHeader->id = LZSS_ID;
  98. pHeader->actualSize = LittleLong( inputLength );
  99. unsigned char *pOutput = pStart + sizeof (lzss_header_t);
  100. const unsigned char *pLookAhead = pInput;
  101. const unsigned char *pWindow = pInput;
  102. const unsigned char *pEncodedPosition = NULL;
  103. unsigned char *pCmdByte = NULL;
  104. int putCmdByte = 0;
  105. while ( inputLength > 0 )
  106. {
  107. pWindow = pLookAhead - m_nWindowSize;
  108. if ( pWindow < pInput )
  109. {
  110. pWindow = pInput;
  111. }
  112. if ( !putCmdByte )
  113. {
  114. pCmdByte = pOutput++;
  115. *pCmdByte = 0;
  116. }
  117. putCmdByte = ( putCmdByte + 1 ) & 0x07;
  118. int encodedLength = 0;
  119. int lookAheadLength = inputLength < LZSS_LOOKAHEAD ? inputLength : LZSS_LOOKAHEAD;
  120. lzss_node_t *pHash = m_pHashTable[pLookAhead[0]].pStart;
  121. while ( pHash )
  122. {
  123. int matchLength = 0;
  124. int length = lookAheadLength;
  125. while ( length-- && pHash->pData[matchLength] == pLookAhead[matchLength] )
  126. {
  127. matchLength++;
  128. }
  129. if ( matchLength > encodedLength )
  130. {
  131. encodedLength = matchLength;
  132. pEncodedPosition = pHash->pData;
  133. }
  134. if ( matchLength == lookAheadLength )
  135. {
  136. break;
  137. }
  138. pHash = pHash->pNext;
  139. }
  140. if ( encodedLength >= 3 )
  141. {
  142. *pCmdByte = ( *pCmdByte >> 1 ) | 0x80;
  143. *pOutput++ = ( ( pLookAhead-pEncodedPosition-1 ) >> LZSS_LOOKSHIFT );
  144. *pOutput++ = ( ( pLookAhead-pEncodedPosition-1 ) << LZSS_LOOKSHIFT ) | ( encodedLength-1 );
  145. }
  146. else
  147. {
  148. encodedLength = 1;
  149. *pCmdByte = ( *pCmdByte >> 1 );
  150. *pOutput++ = *pLookAhead;
  151. }
  152. for ( int i=0; i<encodedLength; i++ )
  153. {
  154. BuildHash( pLookAhead++ );
  155. }
  156. inputLength -= encodedLength;
  157. if ( pOutput >= pEnd )
  158. {
  159. // compression is worse, abandon
  160. return NULL;
  161. }
  162. }
  163. if ( inputLength != 0 )
  164. {
  165. // unexpected failure
  166. Assert( 0 );
  167. return NULL;
  168. }
  169. if ( !putCmdByte )
  170. {
  171. pCmdByte = pOutput++;
  172. *pCmdByte = 0x01;
  173. }
  174. else
  175. {
  176. *pCmdByte = ( ( *pCmdByte >> 1 ) | 0x80 ) >> ( 7 - putCmdByte );
  177. }
  178. *pOutput++ = 0;
  179. *pOutput++ = 0;
  180. if ( pOutputSize )
  181. {
  182. *pOutputSize = pOutput - pStart;
  183. }
  184. return pStart;
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Compress an input buffer. Caller must free output compressed buffer.
  188. // Returns NULL if compression failed (i.e. compression yielded worse results)
  189. //-----------------------------------------------------------------------------
  190. unsigned char* CLZSS::Compress( const unsigned char *pInput, int inputLength, unsigned int *pOutputSize )
  191. {
  192. unsigned char *pStart = (unsigned char *)malloc( inputLength );
  193. unsigned char *pFinal = CompressNoAlloc( pInput, inputLength, pStart, pOutputSize );
  194. if ( !pFinal )
  195. {
  196. free( pStart );
  197. return NULL;
  198. }
  199. return pStart;
  200. }
  201. /*
  202. // BUG BUG: This code is flaky, don't use until it's debugged!!!
  203. unsigned int CLZSS::Uncompress( unsigned char *pInput, CUtlBuffer &buf )
  204. {
  205. int cmdByte = 0;
  206. int getCmdByte = 0;
  207. unsigned int actualSize = GetActualSize( pInput );
  208. if ( !actualSize )
  209. {
  210. // unrecognized
  211. return 0;
  212. }
  213. unsigned char *pBase = ( unsigned char * )buf.Base();
  214. pInput += sizeof( lzss_header_t );
  215. while ( !buf.IsValid() )
  216. {
  217. if ( !getCmdByte )
  218. {
  219. cmdByte = *pInput++;
  220. }
  221. getCmdByte = ( getCmdByte + 1 ) & 0x07;
  222. if ( cmdByte & 0x01 )
  223. {
  224. int position = *pInput++ << LZSS_LOOKSHIFT;
  225. position |= ( *pInput >> LZSS_LOOKSHIFT );
  226. int count = ( *pInput++ & 0x0F ) + 1;
  227. if ( count == 1 )
  228. {
  229. break;
  230. }
  231. unsigned int pos = buf.TellPut();
  232. unsigned char *pSource = ( pBase + pos ) - position - 1;
  233. // BUGBUG:
  234. // This is failing!!!
  235. // buf.WriteBytes( pSource, count );
  236. // So have to iterate them manually
  237. for ( int i =0; i < count; ++i )
  238. {
  239. buf.PutUnsignedChar( *pSource++ );
  240. }
  241. }
  242. else
  243. {
  244. buf.PutUnsignedChar( *pInput++ );
  245. }
  246. cmdByte = cmdByte >> 1;
  247. }
  248. if ( buf.TellPut() != (int)actualSize )
  249. {
  250. // unexpected failure
  251. Assert( 0 );
  252. return 0;
  253. }
  254. return buf.TellPut();
  255. }
  256. */
  257. unsigned int CLZSS::SafeUncompress( const unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize )
  258. {
  259. unsigned int totalBytes = 0;
  260. int cmdByte = 0;
  261. int getCmdByte = 0;
  262. unsigned int actualSize = GetActualSize( pInput );
  263. if ( !actualSize )
  264. {
  265. // unrecognized
  266. return 0;
  267. }
  268. if ( actualSize > unBufSize )
  269. {
  270. return 0;
  271. }
  272. pInput += sizeof( lzss_header_t );
  273. for ( ;; )
  274. {
  275. if ( !getCmdByte )
  276. {
  277. cmdByte = *pInput++;
  278. }
  279. getCmdByte = ( getCmdByte + 1 ) & 0x07;
  280. if ( cmdByte & 0x01 )
  281. {
  282. int position = *pInput++ << LZSS_LOOKSHIFT;
  283. position |= ( *pInput >> LZSS_LOOKSHIFT );
  284. int count = ( *pInput++ & 0x0F ) + 1;
  285. if ( count == 1 )
  286. {
  287. break;
  288. }
  289. unsigned char *pSource = pOutput - position - 1;
  290. if ( totalBytes + count > unBufSize )
  291. {
  292. return 0;
  293. }
  294. for ( int i=0; i<count; i++ )
  295. {
  296. *pOutput++ = *pSource++;
  297. }
  298. totalBytes += count;
  299. }
  300. else
  301. {
  302. if ( totalBytes + 1 > unBufSize )
  303. return 0;
  304. *pOutput++ = *pInput++;
  305. totalBytes++;
  306. }
  307. cmdByte = cmdByte >> 1;
  308. }
  309. if ( totalBytes != actualSize )
  310. {
  311. // unexpected failure
  312. Assert( 0 );
  313. return 0;
  314. }
  315. return totalBytes;
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Uncompress a buffer, Returns the uncompressed size. Caller must provide an
  319. // adequate sized output buffer or memory corruption will occur.
  320. //-----------------------------------------------------------------------------
  321. unsigned int CLZSS::Uncompress( const unsigned char *pInput, unsigned char *pOutput )
  322. {
  323. unsigned int totalBytes = 0;
  324. int cmdByte = 0;
  325. int getCmdByte = 0;
  326. unsigned int actualSize = GetActualSize( pInput );
  327. if ( !actualSize )
  328. {
  329. // unrecognized
  330. return 0;
  331. }
  332. pInput += sizeof( lzss_header_t );
  333. for ( ;; )
  334. {
  335. if ( !getCmdByte )
  336. {
  337. cmdByte = *pInput++;
  338. }
  339. getCmdByte = ( getCmdByte + 1 ) & 0x07;
  340. if ( cmdByte & 0x01 )
  341. {
  342. int position = *pInput++ << LZSS_LOOKSHIFT;
  343. position |= ( *pInput >> LZSS_LOOKSHIFT );
  344. int count = ( *pInput++ & 0x0F ) + 1;
  345. if ( count == 1 )
  346. {
  347. break;
  348. }
  349. unsigned char *pSource = pOutput - position - 1;
  350. for ( int i=0; i<count; i++ )
  351. {
  352. *pOutput++ = *pSource++;
  353. }
  354. totalBytes += count;
  355. }
  356. else
  357. {
  358. *pOutput++ = *pInput++;
  359. totalBytes++;
  360. }
  361. cmdByte = cmdByte >> 1;
  362. }
  363. if ( totalBytes != actualSize )
  364. {
  365. // unexpected failure
  366. Assert( 0 );
  367. return 0;
  368. }
  369. return totalBytes;
  370. }