/* * $Id$ */ /* * xHarbour Project source code: * SIX compatible functions: * hb_LZSSxCompressMem() * hb_LZSSxDecompressMem() * hb_LZSSxCompressFile() * hb_LZSSxDecompressFile() * * SX_FCOMPRESS * SX_FDECOMPRESS * _SX_STRDECOMPRESS * _SX_STRCOMPRESS * * Copyright 2005 Przemyslaw Czerpak * www - http://www.xharbour.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). * * As a special exception, the Harbour Project gives permission for * additional uses of the text contained in its release of Harbour. * * The exception is that, if you link the Harbour libraries with other * files to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the Harbour library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by the Harbour * Project under the name Harbour. If you copy code from other * Harbour Project or Free Software Foundation releases into a copy of * Harbour, as the General Public License permits, the exception does * not apply to the code that you add in this way. To avoid misleading * anyone as to the status of such modified files, you must delete * this exception notice from them. * * If you write modifications of your own for Harbour, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. * */ /* SIX uses modified version of LZSS algorithm. It uses 12 bits for position offset in ring buffer and 4 bits for the match length. LZSS is modified version of LZ77 algorithm which can store original bytes from input string instead of position and match length in the ring buffer when the match length does not reach some limit. In SIX the minimum match length is 3 and it is used to increase to match length in 4 bit offset by adding 3 also so the efective maximum match length is 18. Of course we have to store the information about the type of item in compressed data to know it is (offset+length) pair or simple byte. SIX put 1 byte in compressed data which inform about the type of next 8 items: bit set to 1 means normal byte, 0 means item pair. The funny thing is that SIX seems to effectively use only 11 bits ring buffer (2048 bytes size). I still do not know why. Maybe it was a hack for some problems with implementation of encoding or work around for a bug in some signed/unsigned value conversions? Or maybe someone wanted to reduce memory overhead in used algorithm for finding the longest match? The SIX was written for 16-bit DOS and the memory consumption was important though it should not be too much. They documented ~9KB increasing the ring buffer size should be linear to other used (helper) structures. The next interesting thing is that it dynamically overwrites the ring buffer with stream data and does not use any separate look ahead buffers. This fact suggests that SIX uses some simple implementation with greedy early parsing and does not look for optimal matches. Our algorithm has to make the same with the ring buffer to be compatible. UPDATE: Using smaller ring buffer without increasing the match pointer suggested me that it is possible that someone didn't understand it fully and modified already existing algorithm. I spend a while with a google looking for old LZSS implementations and I've found it. IMHO in 99% this code is used in SIX. This is lzss.c file written by Haruhiko Okumura with the following note in header: ,--------------------------------------------------------- | Use, distribute, and modify this program freely. | Please send me your improved versions. `--------------------------------------------------------- This LZSS implementation gives exactly the same results as SIX when we change the ring buffer size (#define N 4096 //in the head of this file) to 2048. So I used exactly the same algorithm with binary tree for finding the longest match. The code may looks a little bit differ then in lzss.c because I've already implement most of it before I found this file and I didn't want to remove it but the original author is Haruhiko Okumura. The text above allow us use it in [x]Harbour. This version isn't for sure improved due to SIX modifications so I do not think I should send it to him. IMHO it is also less usable for other things then strict SIX compatibility - we already have much stronger compressions in ZLIB. After the LZSS compression the result could be longer then original strings. It looks that SIX return original string when the compressed one is longer then original + 253. I do not like it because there is no marker which informs if string was compressed or not and calling decompression procedure for uncompressed data gives broken results. I do not want to make the same. When file is compressed then SIX put the uncompressed file size in first four bytes. Similar situation is with string returned by sx_compress() - first four bytes is uncompressed size in little endian order. This has to be done in upper level functions, hb_LZSSxCompressMem() and hb_LZSSxCompressFile() intentionally do not do that. */ #include "hbsxfunc.h" /* number of bits for encoded item (position,length) */ #define ITEMBITS 16 /* unused DUMMY bits - who does know why SIX has it? */ #define DUMMYBITS 1 /* number of bits for position offset */ #define OFFSETBITS (12 - DUMMYBITS) /* the minimum match length to encode as new position */ #define MINLENGTH 3 /* number of bits for match length, 1 bit reserved for ITEM type */ #define LENGTHBITS ( ITEMBITS - OFFSETBITS - DUMMYBITS ) /* the maximum match length we can encode in LENGTHBITS */ #define MAXLENGTH ( ( 1 << LENGTHBITS ) + MINLENGTH - 1 ) /* size of ring buffer */ #define RBUFLENGTH ( 1 << OFFSETBITS ) /* the bit mask for ring buffer */ #define RBUFMASK ( ( 1 << OFFSETBITS ) - 1 ) /* the bit mask for match length */ #define MATCHMASK ( ( 1 << LENGTHBITS ) - 1 ) /* get ringbuffer index */ #define RBUFINDEX(i) ( ( i ) & RBUFMASK ) /* get ring buffer offset position from low and high bytes */ #define LZSS_OFFSET(l, h) ( (l) | ( (h & ~MATCHMASK) << ( 8 - LENGTHBITS ) ) ) /* get match length from low and high byte */ #define LZSS_LENGTH(l, h) ( ( (h) & MATCHMASK ) + MINLENGTH ) /* create compressed item from match position and length */ #define LZSS_ITEM(o, l) ( ( (o) << LENGTHBITS ) | ( (l) - MINLENGTH ) ) /* create low byte of compressed item */ #define LZSS_ITMLO(o, l) ( ( BYTE ) (o) ) /* create high byte of compressed item */ #define LZSS_ITMHI(o, l) ( ( BYTE ) ( ( ( (o) >> ( 8 - LENGTHBITS ) ) & ~MATCHMASK ) | \ ( (l) - MINLENGTH ) ) ) /* maximum size of item set: byte with item type bits plus 8 items */ #define ITEMSETSIZE ( ( ITEMBITS << 3 ) + 1 ) /* the size of IO buffer for file (de)compression */ #define LZSS_IOBUFLEN 8192 /* uninitialized (dummy) node in compression trees */ #define DUMMYNODE RBUFLENGTH typedef struct _HB_LZSSX_COMPR { FHANDLE hInput; BYTE * inBuffer; ULONG inBuffSize; ULONG inBuffPos; ULONG inBuffRead; BOOL fInFree; FHANDLE hOutput; BYTE * outBuffer; ULONG outBuffSize; ULONG outBuffPos; BOOL fOutFree; ULONG ulMaxSize; ULONG ulOutSize; BOOL fResult; BOOL fContinue; BYTE ring_buffer[ RBUFLENGTH + MAXLENGTH - 1 ]; SHORT match_offset; SHORT match_length; SHORT parent[ RBUFLENGTH + 1 ]; SHORT left [ RBUFLENGTH + 1 ]; SHORT right [ RBUFLENGTH + 257 ]; } HB_LZSSX_COMPR; typedef HB_LZSSX_COMPR * PHB_LZSSX_COMPR; void hb_LZSSxExit( PHB_LZSSX_COMPR pCompr ) { if( pCompr->fInFree ) hb_xfree( pCompr->inBuffer ); if( pCompr->fOutFree ) hb_xfree( pCompr->outBuffer ); hb_xfree( pCompr ); } static PHB_LZSSX_COMPR hb_LZSSxInit( FHANDLE hInput, BYTE * pSrcBuf, ULONG ulSrcBuf, FHANDLE hOutput, BYTE * pDstBuf, ULONG ulDstBuf ) { PHB_LZSSX_COMPR pCompr = ( PHB_LZSSX_COMPR ) hb_xgrab( sizeof( HB_LZSSX_COMPR ) ); if( hInput != FS_ERROR && ulSrcBuf == 0 ) ulSrcBuf = LZSS_IOBUFLEN; if( hOutput != FS_ERROR && ulDstBuf == 0 ) ulDstBuf = LZSS_IOBUFLEN; pCompr->hInput = hInput; pCompr->inBuffer = pSrcBuf; pCompr->inBuffSize = ulSrcBuf; pCompr->inBuffPos = 0; pCompr->inBuffRead = ( hInput == FS_ERROR ) ? ulSrcBuf : 0; pCompr->fInFree = ( hInput != FS_ERROR && pSrcBuf == NULL ); pCompr->hOutput = hOutput; pCompr->outBuffer = pDstBuf; pCompr->outBuffSize = ulDstBuf; pCompr->outBuffPos = 0; pCompr->fOutFree = ( hOutput != FS_ERROR && pDstBuf == NULL ); pCompr->ulMaxSize = 0; pCompr->ulOutSize = 0; pCompr->fResult = TRUE; pCompr->fContinue = FALSE; if( pCompr->fInFree ) pCompr->inBuffer = ( BYTE * ) hb_xgrab( ulDstBuf ); if( pCompr->fOutFree ) pCompr->outBuffer = ( BYTE * ) hb_xgrab( ulDstBuf ); /* initialize the ring buffer with spaces, because SIX uses dynamic ring buffer then we do not have to fill last MAXLENGTH characters */ memset( pCompr->ring_buffer, ' ', RBUFLENGTH - MAXLENGTH ); return pCompr; } static BOOL hb_LZSSxFlush( PHB_LZSSX_COMPR pCompr ) { if( pCompr->fResult && pCompr->hOutput != FS_ERROR ) { if( hb_fsWriteLarge( pCompr->hOutput, pCompr->outBuffer, pCompr->outBuffPos ) != pCompr->outBuffPos ) { pCompr->fResult = FALSE; } else { pCompr->ulOutSize += pCompr->outBuffPos; pCompr->outBuffPos = 0; } } return pCompr->fResult; } static BOOL hb_LZSSxWrite( PHB_LZSSX_COMPR pCompr, BYTE bVal ) { if( pCompr->fResult ) { if( pCompr->outBuffPos == pCompr->outBuffSize ) hb_LZSSxFlush( pCompr ); if( pCompr->outBuffPos < pCompr->outBuffSize ) pCompr->outBuffer[pCompr->outBuffPos] = bVal; else pCompr->fResult = FALSE; } pCompr->outBuffPos++; return pCompr->fResult || pCompr->fContinue; } static int hb_LZSSxRead( PHB_LZSSX_COMPR pCompr ) { if( pCompr->inBuffPos < pCompr->inBuffRead ) return pCompr->inBuffer[ pCompr->inBuffPos++ ]; if( pCompr->hInput != FS_ERROR ) { pCompr->inBuffRead = hb_fsReadLarge( pCompr->hInput, pCompr->inBuffer, pCompr->inBuffSize ); pCompr->inBuffPos = 0; if( pCompr->inBuffPos < pCompr->inBuffRead ) return pCompr->inBuffer[ pCompr->inBuffPos++ ]; } return -1; } static BOOL hb_LZSSxDecode( PHB_LZSSX_COMPR pCompr ) { BOOL fResult = TRUE; USHORT itemMask; int offset, length, index, c, h; index = RBUFLENGTH - MAXLENGTH; itemMask = 0; do { itemMask >>= 1; /* Is the next character bitfield with type of next 8 items ? */ if( ( itemMask & 0x0100 ) == 0 ) { if( ( c = hb_LZSSxRead( pCompr ) ) == -1 ) break; /* simple trick to reduce number of shift operations */ itemMask = c | 0xff00; } if( ( c = hb_LZSSxRead( pCompr ) ) == -1 ) break; if( itemMask & 1 ) /* Is the next character normal byte ? */ { if( ! hb_LZSSxWrite( pCompr, ( BYTE ) c ) ) { fResult = FALSE; break; } pCompr->ring_buffer[ index ] = ( BYTE ) c; index = RBUFINDEX( index + 1 ); } else /* we have an item pair (ring buffer offset : match length) */ { if( ( h = hb_LZSSxRead( pCompr ) ) == -1 ) { /* fResult = FALSE; */ break; } offset = LZSS_OFFSET( c, h ); /* get offset to ring buffer */ length = LZSS_LENGTH( c, h ); /* get match length */ for( h = 0; h < length; h++ ) { c = pCompr->ring_buffer[ RBUFINDEX( offset + h ) ]; if( ! hb_LZSSxWrite( pCompr, ( BYTE ) c ) ) { fResult = FALSE; break; } /* SIX does not use additional buffers and dynamically overwrite the ring buffer - we have to make exactly the same or our results will be differ when abs( offset - index ) < length */ pCompr->ring_buffer[ index ] = ( BYTE ) c; index = RBUFINDEX( index + 1 ); } } } while( fResult ); if( fResult ) fResult = hb_LZSSxFlush( pCompr ); return fResult; } static void hb_LZSSxNodeInsert( PHB_LZSSX_COMPR pCompr, int r ) { int i, p, cmp; unsigned char *key; cmp = 1; key = &pCompr->ring_buffer[ r ]; p = RBUFLENGTH + 1 + key[ 0 ]; pCompr->right[ r ] = pCompr->left[ r ] = DUMMYNODE; pCompr->match_length = 0; for( ; ; ) { if( cmp >= 0 ) { if( pCompr->right[ p ] != DUMMYNODE ) p = pCompr->right[ p ]; else { pCompr->right[ p ] = r; pCompr->parent[ r ] = p; return; } } else { if( pCompr->left[ p ] != DUMMYNODE ) p = pCompr->left[ p ]; else { pCompr->left[ p ] = r; pCompr->parent[ r ] = p; return; } } for( i = 1; i < MAXLENGTH; i++ ) { if( ( cmp = key[ i ] - pCompr->ring_buffer[ p + i ] ) != 0 ) break; } if( i > pCompr->match_length ) { pCompr->match_offset = p; pCompr->match_length = i; if( i >= MAXLENGTH ) break; } } pCompr->parent[ r ] = pCompr->parent[ p ]; pCompr->left[ r ] = pCompr->left[ p ]; pCompr->right[ r ] = pCompr->right[ p ]; pCompr->parent[ pCompr->left[ p ] ] = r; pCompr->parent[ pCompr->right[ p ] ] = r; if( pCompr->right[ pCompr->parent[ p ] ] == p ) pCompr->right[ pCompr->parent[ p ] ] = r; else pCompr->left[ pCompr->parent[ p ] ] = r; pCompr->parent[ p ] = DUMMYNODE; } static void hb_LZSSxNodeDelete( PHB_LZSSX_COMPR pCompr, int p ) { if( pCompr->parent[ p ] != DUMMYNODE ) { int q; if( pCompr->right[ p ] == DUMMYNODE ) q = pCompr->left[ p ]; else if( pCompr->left[ p ] == DUMMYNODE ) q = pCompr->right[ p ]; else { q = pCompr->left[ p ]; if( pCompr->right[ q ] != DUMMYNODE ) { do { q = pCompr->right[ q ]; } while( pCompr->right[ q ] != DUMMYNODE ); pCompr->right[ pCompr->parent[ q ] ] = pCompr->left[ q ]; pCompr->parent[ pCompr->left[ q ] ] = pCompr->parent[ q ]; pCompr->left[ q ] = pCompr->left[ p ]; pCompr->parent[ pCompr->left[ p ] ] = q; } pCompr->right[ q ] = pCompr->right[ p ]; pCompr->parent[ pCompr->right[ p ] ] = q; } pCompr->parent[ q ] = pCompr->parent[ p ]; if( pCompr->right[ pCompr->parent[ p ] ] == p ) pCompr->right[ pCompr->parent[ p ] ] = q; else pCompr->left[ pCompr->parent[ p ] ] = q; pCompr->parent[ p ] = DUMMYNODE; } } static ULONG hb_LZSSxEncode( PHB_LZSSX_COMPR pCompr ) { BYTE itemSet[ITEMSETSIZE]; BYTE itemMask; ULONG ulSize = 0; int iItem; short int i, c, len, r, s, last_match_length; for( i = RBUFLENGTH + 1; i < RBUFLENGTH + 257; i++ ) pCompr->right[i] = DUMMYNODE; for( i = 0; i < RBUFLENGTH; i++ ) pCompr->parent[ i ] = DUMMYNODE; itemSet[ 0 ] = 0; iItem = itemMask = 1; s = 0; r = RBUFLENGTH - MAXLENGTH; for( len = 0; len < MAXLENGTH; len++ ) { if( ( c = hb_LZSSxRead( pCompr ) ) == -1 ) break; pCompr->ring_buffer[ r + len ] = c; } if( len == 0 ) return ulSize; for( i = 1; i <= MAXLENGTH; i++ ) hb_LZSSxNodeInsert( pCompr, r - i ); hb_LZSSxNodeInsert( pCompr, r ); do { if( pCompr->match_length > len ) pCompr->match_length = len; if( pCompr->match_length < MINLENGTH ) { pCompr->match_length = 1; itemSet[ 0 ] |= itemMask; itemSet[ iItem++ ] = pCompr->ring_buffer[ r ]; } else { itemSet[iItem++] = LZSS_ITMLO( pCompr->match_offset, pCompr->match_length ); itemSet[iItem++] = LZSS_ITMHI( pCompr->match_offset, pCompr->match_length ); } if( ( itemMask <<= 1 ) == 0 ) { for( i = 0; i < iItem; i++ ) { if( !hb_LZSSxWrite( pCompr, itemSet[ i ] ) ) return ( ULONG ) -1; } ulSize += iItem; itemSet[ 0 ] = 0; iItem = itemMask = 1; } last_match_length = pCompr->match_length; for( i = 0; i < last_match_length && ( c = hb_LZSSxRead( pCompr ) ) != -1; i++ ) { hb_LZSSxNodeDelete( pCompr, s ); pCompr->ring_buffer[ s ] = c; if( s < MAXLENGTH - 1 ) pCompr->ring_buffer[ s + RBUFLENGTH ] = c; s = RBUFINDEX( s + 1 ); r = RBUFINDEX( r + 1 ); hb_LZSSxNodeInsert( pCompr, r ); } while( i++ < last_match_length ) { hb_LZSSxNodeDelete( pCompr, s ); s = RBUFINDEX( s + 1 ); r = RBUFINDEX( r + 1 ); if( --len ) hb_LZSSxNodeInsert( pCompr, r ); } } while( len > 0 ); if( iItem > 1 ) { for( i = 0; i < iItem; i++ ) { if( !hb_LZSSxWrite( pCompr, itemSet[ i ] ) ) return ( ULONG ) -1; } ulSize += iItem; } if( !hb_LZSSxFlush( pCompr ) ) return ( ULONG ) -1; return ulSize; } BOOL hb_LZSSxCompressMem( BYTE * pSrcBuf, ULONG ulSrcLen, BYTE * pDstBuf, ULONG ulDstLen, ULONG * pulSize ) { PHB_LZSSX_COMPR pCompr; ULONG ulSize; pCompr = hb_LZSSxInit( FS_ERROR, pSrcBuf, ulSrcLen, FS_ERROR, pDstBuf, ulDstLen ); ulSize = hb_LZSSxEncode( pCompr ); hb_LZSSxExit( pCompr ); if( pulSize ) *pulSize = ulSize; return ( ulSize <= ulDstLen ); } BOOL hb_LZSSxDecompressMem( BYTE * pSrcBuf, ULONG ulSrcLen, BYTE * pDstBuf, ULONG ulDstLen ) { PHB_LZSSX_COMPR pCompr; BOOL fResult; pCompr = hb_LZSSxInit( FS_ERROR, pSrcBuf, ulSrcLen, FS_ERROR, pDstBuf, ulDstLen ); fResult = hb_LZSSxDecode( pCompr ); hb_LZSSxExit( pCompr ); return fResult; } BOOL hb_LZSSxCompressFile( FHANDLE hInput, FHANDLE hOutput, ULONG * pulSize ) { PHB_LZSSX_COMPR pCompr; ULONG ulSize; pCompr = hb_LZSSxInit( hInput, NULL, 0, hOutput, NULL, 0 ); ulSize = hb_LZSSxEncode( pCompr ); hb_LZSSxExit( pCompr ); if( pulSize ) *pulSize = ulSize; return ulSize != ( ULONG ) -1; } BOOL hb_LZSSxDecompressFile( FHANDLE hInput, FHANDLE hOutput ) { PHB_LZSSX_COMPR pCompr; BOOL fResult; pCompr = hb_LZSSxInit( hInput, NULL, 0, hOutput, NULL, 0 ); fResult = hb_LZSSxDecode( pCompr ); hb_LZSSxExit( pCompr ); return fResult; } HB_FUNC( SX_FCOMPRESS ) { BOOL fRet = FALSE; FHANDLE hInput, hOutput; char * szSource = hb_parc( 1 ), * szDestin = hb_parc( 2 ); BYTE buf[ 4 ]; ULONG ulSize; if( szSource && *szSource && szDestin && *szDestin ) { hInput = hb_fsExtOpen( ( BYTE * ) szSource, NULL, FO_READ | FO_DENYNONE | FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL ); if( hInput != FS_ERROR ) { hOutput = hb_fsExtOpen( ( BYTE * ) szDestin, NULL, FO_READWRITE | FO_EXCLUSIVE | FXO_TRUNCATE | FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL ); if( hOutput != FS_ERROR ) { /* store uncompressed file size in first 4 bytes of destination * file in little endian order - for SIX3 compatibility */ ulSize = hb_fsSeek( hInput, 0, FS_END ); if( hb_fsSeek( hInput, 0, FS_SET ) == 0 ) { HB_PUT_LE_UINT32( buf, ulSize ); if( hb_fsWrite( hOutput, buf, 4 ) == 4 ) fRet = hb_LZSSxCompressFile( hInput, hOutput, NULL ); } hb_fsClose( hOutput ); } hb_fsClose( hInput ); } } hb_retl( fRet ); } HB_FUNC( SX_FDECOMPRESS ) { BOOL fRet = FALSE; FHANDLE hInput, hOutput; char * szSource = hb_parc( 1 ), * szDestin = hb_parc( 2 ); if( szSource && *szSource && szDestin && *szDestin ) { hInput = hb_fsExtOpen( ( BYTE * ) szSource, NULL, FO_READ | FO_DENYNONE | FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL ); if( hInput != FS_ERROR ) { hOutput = hb_fsExtOpen( ( BYTE * ) szDestin, NULL, FO_READWRITE | FO_EXCLUSIVE | FXO_TRUNCATE | FXO_DEFAULTS | FXO_SHARELOCK, NULL, NULL ); if( hOutput != FS_ERROR ) { /* skip the four bytes with original file length */ if( hb_fsSeek( hInput, 4, FS_SET ) == 4 ) fRet = hb_LZSSxDecompressFile( hInput, hOutput ); hb_fsClose( hOutput ); } hb_fsClose( hInput ); } } hb_retl( fRet ); } HB_FUNC( _SX_STRCOMPRESS ) { BOOL fOK = FALSE; BYTE * pStr = ( BYTE * ) hb_parc( 1 ), * pBuf; if( pStr ) { ULONG ulLen = hb_parclen( 1 ), ulBuf, ulDst; /* this is for strict SIX compatibility - in general very bad idea */ ulBuf = ulLen + 257; pBuf = ( BYTE * ) hb_xgrab( ulBuf ); HB_PUT_LE_UINT32( pBuf, ulLen ); fOK = hb_LZSSxCompressMem( pStr, ulLen, pBuf + 4, ulBuf - 4, &ulDst ); if( fOK ) hb_retclen( ( char * ) pBuf, ulDst + 4 ); hb_xfree( pBuf ); } if( !fOK ) hb_itemReturn( hb_param( 1, HB_IT_ANY ) ); } HB_FUNC( _SX_STRDECOMPRESS ) { BOOL fOK = FALSE; BYTE * pStr = ( BYTE * ) hb_parc( 1 ), * pBuf; if( pStr ) { ULONG ulLen = hb_parclen( 1 ), ulBuf; if( ulLen >= 4 ) { ulBuf = HB_GET_LE_UINT32( pStr ); pBuf = ( BYTE * ) hb_xalloc( ulBuf ); if( pBuf ) { fOK = hb_LZSSxDecompressMem( pStr + 4, ulLen - 4, pBuf, ulBuf ); if( fOK ) hb_retclen( ( char * ) pBuf, ulBuf ); hb_xfree( pBuf ); } else { PHB_ITEM pItem = hb_errRT_SubstParams( "SIXCOMPRESS", EG_MEM, 0, "possible compressed string corruption", "_SX_STRDECOMPRESS" ); hb_itemReturn( pItem ); hb_itemRelease( pItem ); return; } } } if( !fOK ) hb_itemReturn( hb_param( 1, HB_IT_ANY ) ); }