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.

425 lines
9.3 KiB

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