251 lines
8.5 KiB
C
251 lines
8.5 KiB
C
#include "CMP_Pub.h"
|
|
|
|
#if defined(D_CMP_LZSS)
|
|
|
|
#include <stdio.h>
|
|
#include "CMP_CPA.h"
|
|
#include "ErmCmp.h"
|
|
#include "CMP_File.h"
|
|
|
|
/**************************************************************************/
|
|
#define C_CMP_LZSS_INDEX_BIT_COUNT 12
|
|
#define C_CMP_LZSS_LENGTH_BIT_COUNT 4
|
|
#define C_CMP_LZSS_WINDOW_SIZE ( 1 << C_CMP_LZSS_INDEX_BIT_COUNT )
|
|
#define C_CMP_LZSS_RAW_LOOK_AHEAD_SIZE ( 1 << C_CMP_LZSS_LENGTH_BIT_COUNT )
|
|
#define C_CMP_LZSS_BREAK_EVEN ( ( 1 + C_CMP_LZSS_INDEX_BIT_COUNT + C_CMP_LZSS_LENGTH_BIT_COUNT ) / 9 )
|
|
#define C_CMP_LZSS_LOOK_AHEAD_SIZE ( C_CMP_LZSS_RAW_LOOK_AHEAD_SIZE + C_CMP_LZSS_BREAK_EVEN )
|
|
#define C_CMP_LZSS_TREE_ROOT C_CMP_LZSS_WINDOW_SIZE
|
|
#define C_CMP_LZSS_END_OF_STREAM 0
|
|
#define C_CMP_LZSS_UNUSED 0
|
|
|
|
/**************************************************************************/
|
|
|
|
#define M_CMP_LZSS_MOD_WINDOW( a ) ( ( a ) & ( C_CMP_LZSS_WINDOW_SIZE - 1 ) )
|
|
|
|
/**************************************************************************/
|
|
|
|
unsigned char CMP_LZSS_g_a_ucWindow[C_CMP_LZSS_WINDOW_SIZE];
|
|
|
|
struct
|
|
{
|
|
int parent;
|
|
int smaller_child;
|
|
int larger_child;
|
|
} CMP_LZSS_g_a_stTree[C_CMP_LZSS_WINDOW_SIZE + 1 ];
|
|
|
|
char *CMP_LZSS_CompressionName = "LZSS method";
|
|
|
|
/**************************************************************************/
|
|
|
|
void CMP_LZSS_InitTree( int r );
|
|
void CMP_LZSS_ContractNode( int old_node, int new_node );
|
|
void CMP_LZSS_ReplaceNode( int old_node, int new_node );
|
|
int CMP_LZSS_FindNextNode( int node );
|
|
void CMP_LZSS_DeleteString( int p );
|
|
int CMP_LZSS_AddString( int new_node, int *match_position );
|
|
|
|
/**************************************************************************/
|
|
|
|
void CMP_LZSS_fn_vInitTree( int r )
|
|
{
|
|
CMP_LZSS_g_a_stTree[ C_CMP_LZSS_TREE_ROOT ].larger_child = r;
|
|
CMP_LZSS_g_a_stTree[ r ].parent = C_CMP_LZSS_TREE_ROOT;
|
|
CMP_LZSS_g_a_stTree[ r ].larger_child = C_CMP_LZSS_UNUSED;
|
|
CMP_LZSS_g_a_stTree[ r ].smaller_child = C_CMP_LZSS_UNUSED;
|
|
}
|
|
/**************************************************************************/
|
|
void CMP_LZSS_fn_vContractNode( int old_node , int new_node )
|
|
{
|
|
CMP_LZSS_g_a_stTree[ new_node ].parent = CMP_LZSS_g_a_stTree[ old_node ].parent;
|
|
if ( CMP_LZSS_g_a_stTree[ CMP_LZSS_g_a_stTree[ old_node ].parent ].larger_child == old_node )
|
|
CMP_LZSS_g_a_stTree[ CMP_LZSS_g_a_stTree[ old_node ].parent ].larger_child = new_node;
|
|
else
|
|
CMP_LZSS_g_a_stTree[ CMP_LZSS_g_a_stTree[ old_node ].parent ].smaller_child = new_node;
|
|
CMP_LZSS_g_a_stTree[ old_node ].parent = C_CMP_LZSS_UNUSED;
|
|
}
|
|
/**************************************************************************/
|
|
void CMP_LZSS_fn_vReplaceNode( int old_node , int new_node )
|
|
{
|
|
int parent;
|
|
|
|
parent = CMP_LZSS_g_a_stTree[ old_node ].parent;
|
|
if ( CMP_LZSS_g_a_stTree[ parent ].smaller_child == old_node )
|
|
CMP_LZSS_g_a_stTree[ parent ].smaller_child = new_node;
|
|
else
|
|
CMP_LZSS_g_a_stTree[ parent ].larger_child = new_node;
|
|
CMP_LZSS_g_a_stTree[ new_node ] = CMP_LZSS_g_a_stTree[ old_node ];
|
|
CMP_LZSS_g_a_stTree[ CMP_LZSS_g_a_stTree[ new_node ].smaller_child ].parent = new_node;
|
|
CMP_LZSS_g_a_stTree[ CMP_LZSS_g_a_stTree[ new_node ].larger_child ].parent = new_node;
|
|
CMP_LZSS_g_a_stTree[ old_node ].parent = C_CMP_LZSS_UNUSED;
|
|
}
|
|
/**************************************************************************/
|
|
int CMP_LZSS_fn_vFindNextNode( int node )
|
|
{
|
|
int next;
|
|
|
|
next = CMP_LZSS_g_a_stTree[ node ].smaller_child;
|
|
while ( CMP_LZSS_g_a_stTree[ next ].larger_child != C_CMP_LZSS_UNUSED )
|
|
next = CMP_LZSS_g_a_stTree[ next ].larger_child;
|
|
return( next );
|
|
}
|
|
/**************************************************************************/
|
|
void CMP_LZSS_fn_vDeleteString( int p )
|
|
{
|
|
int replacement;
|
|
|
|
if ( CMP_LZSS_g_a_stTree[ p ].parent == C_CMP_LZSS_UNUSED )
|
|
return;
|
|
if ( CMP_LZSS_g_a_stTree[ p ].larger_child == C_CMP_LZSS_UNUSED )
|
|
CMP_LZSS_fn_vContractNode( p, CMP_LZSS_g_a_stTree[ p ].smaller_child );
|
|
else if ( CMP_LZSS_g_a_stTree[ p ].smaller_child == C_CMP_LZSS_UNUSED )
|
|
CMP_LZSS_fn_vContractNode( p, CMP_LZSS_g_a_stTree[ p ].larger_child );
|
|
else
|
|
{
|
|
replacement = CMP_LZSS_fn_vFindNextNode( p );
|
|
CMP_LZSS_fn_vDeleteString( replacement );
|
|
CMP_LZSS_fn_vReplaceNode( p, replacement );
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
int CMP_LZSS_fn_vAddString( int new_node , int *match_position )
|
|
{
|
|
int i;
|
|
int test_node;
|
|
int delta = 0;
|
|
int match_length;
|
|
int *child;
|
|
|
|
if ( new_node == C_CMP_LZSS_END_OF_STREAM )
|
|
return( 0 );
|
|
test_node = CMP_LZSS_g_a_stTree[ C_CMP_LZSS_TREE_ROOT ].larger_child;
|
|
match_length = 0;
|
|
for ( ; ; )
|
|
{
|
|
for ( i = 0 ; i < C_CMP_LZSS_LOOK_AHEAD_SIZE ; i++ )
|
|
{
|
|
delta = CMP_LZSS_g_a_ucWindow[ M_CMP_LZSS_MOD_WINDOW( new_node + i ) ] - CMP_LZSS_g_a_ucWindow[ M_CMP_LZSS_MOD_WINDOW( test_node + i ) ];
|
|
if ( delta != 0 )
|
|
break;
|
|
}
|
|
if ( i >= match_length )
|
|
{
|
|
match_length = i;
|
|
*match_position = test_node;
|
|
if ( match_length >= C_CMP_LZSS_LOOK_AHEAD_SIZE )
|
|
{
|
|
CMP_LZSS_fn_vReplaceNode( test_node, new_node );
|
|
return( match_length );
|
|
}
|
|
}
|
|
if ( delta >= 0 )
|
|
child = &CMP_LZSS_g_a_stTree[ test_node ].larger_child;
|
|
else
|
|
child = &CMP_LZSS_g_a_stTree[ test_node ].smaller_child;
|
|
if ( *child == C_CMP_LZSS_UNUSED )
|
|
{
|
|
*child = new_node;
|
|
CMP_LZSS_g_a_stTree[ new_node ].parent = test_node;
|
|
CMP_LZSS_g_a_stTree[ new_node ].larger_child = C_CMP_LZSS_UNUSED;
|
|
CMP_LZSS_g_a_stTree[ new_node ].smaller_child = C_CMP_LZSS_UNUSED;
|
|
return( match_length );
|
|
}
|
|
test_node = *child;
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
void CMP_LZSS_fn_vCompressFile( FILE *input , tdstBitFile *output )
|
|
{
|
|
int i;
|
|
int c;
|
|
int look_ahead_bytes;
|
|
int current_position;
|
|
int replace_count;
|
|
int match_length;
|
|
int match_position;
|
|
|
|
current_position = 1;
|
|
for ( i = 0 ; i < C_CMP_LZSS_LOOK_AHEAD_SIZE ; i++ )
|
|
{
|
|
if ( ( c = getc( input ) ) == EOF )
|
|
break;
|
|
CMP_LZSS_g_a_ucWindow[ current_position + i ] = (unsigned char) c;
|
|
}
|
|
look_ahead_bytes = i;
|
|
CMP_LZSS_fn_vInitTree( current_position );
|
|
match_length = 0;
|
|
match_position = 0;
|
|
while ( look_ahead_bytes > 0 )
|
|
{
|
|
if ( match_length > look_ahead_bytes )
|
|
match_length = look_ahead_bytes;
|
|
if ( match_length <= C_CMP_LZSS_BREAK_EVEN )
|
|
{
|
|
replace_count = 1;
|
|
CMP_fn_vOutputBit( output, 1 );
|
|
CMP_fn_vOutputBits( output,
|
|
(unsigned long) CMP_LZSS_g_a_ucWindow[ current_position ], 8 );
|
|
}
|
|
else
|
|
{
|
|
CMP_fn_vOutputBit( output, 0 );
|
|
CMP_fn_vOutputBits( output,
|
|
(unsigned long) match_position, C_CMP_LZSS_INDEX_BIT_COUNT );
|
|
CMP_fn_vOutputBits( output,
|
|
(unsigned long) ( match_length - ( C_CMP_LZSS_BREAK_EVEN + 1 ) ),
|
|
C_CMP_LZSS_LENGTH_BIT_COUNT );
|
|
replace_count = match_length;
|
|
}
|
|
for ( i = 0 ; i < replace_count ; i++ )
|
|
{
|
|
CMP_LZSS_fn_vDeleteString( M_CMP_LZSS_MOD_WINDOW( current_position + C_CMP_LZSS_LOOK_AHEAD_SIZE ) );
|
|
if ( ( c = getc( input ) ) == EOF )
|
|
look_ahead_bytes--;
|
|
else
|
|
CMP_LZSS_g_a_ucWindow[ M_CMP_LZSS_MOD_WINDOW( current_position + C_CMP_LZSS_LOOK_AHEAD_SIZE ) ] = (unsigned char) c;
|
|
current_position = M_CMP_LZSS_MOD_WINDOW( current_position + 1 );
|
|
if ( look_ahead_bytes )
|
|
match_length = CMP_LZSS_fn_vAddString( current_position, &match_position );
|
|
}
|
|
}
|
|
CMP_fn_vOutputBit( output, 0 );
|
|
CMP_fn_vOutputBits( output, (unsigned long) C_CMP_LZSS_END_OF_STREAM, C_CMP_LZSS_INDEX_BIT_COUNT );
|
|
}
|
|
/**************************************************************************/
|
|
void CMP_LZSS_fn_vExpandFile( tdstBitFile *input , FILE *output )
|
|
{
|
|
int i;
|
|
int current_position;
|
|
int c;
|
|
int match_length;
|
|
int match_position;
|
|
|
|
current_position = 1;
|
|
for ( ; ; )
|
|
{
|
|
if ( CMP_fn_lInputBit( input ) )
|
|
{
|
|
c = (int) CMP_fn_ulInputBits( input, 8 );
|
|
putc( c, output );
|
|
CMP_LZSS_g_a_ucWindow[ current_position ] = (unsigned char) c;
|
|
current_position = M_CMP_LZSS_MOD_WINDOW( current_position + 1 );
|
|
}
|
|
else
|
|
{
|
|
match_position = (int) CMP_fn_ulInputBits( input, C_CMP_LZSS_INDEX_BIT_COUNT );
|
|
if ( match_position == C_CMP_LZSS_END_OF_STREAM )
|
|
break;
|
|
match_length = (int) CMP_fn_ulInputBits( input, C_CMP_LZSS_LENGTH_BIT_COUNT );
|
|
match_length += C_CMP_LZSS_BREAK_EVEN;
|
|
for ( i = 0 ; i <= match_length ; i++ )
|
|
{
|
|
c = CMP_LZSS_g_a_ucWindow[ M_CMP_LZSS_MOD_WINDOW( match_position + i ) ];
|
|
putc( c, output );
|
|
CMP_LZSS_g_a_ucWindow[ current_position ] = (unsigned char) c;
|
|
current_position = M_CMP_LZSS_MOD_WINDOW( current_position + 1 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**************************************************************************/
|
|
#endif /* D_CMP_LZSS */
|