mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-29 22:34:13 +08:00
834 lines
29 KiB
C#
834 lines
29 KiB
C#
//! \file KogadoCocotte.cs
|
|
//! \date Mon Aug 25 13:35:37 2014
|
|
//! \brief Kogado engine Cocotte compression/encryption implementation.
|
|
//
|
|
// 作者について:
|
|
// あんたいとるどどきゅめんと < http://juicy.s53.xrea.com/program/ >
|
|
// Written by juicy.gt < juicy[atm@rk]s53.xrea.com >
|
|
//
|
|
// Original code licensed under GNU GPL Version 2
|
|
//
|
|
// C# port by mørkt
|
|
// 2014/08/25
|
|
//
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace GameRes.Formats.Kogado
|
|
{
|
|
public class CocotteEncoder
|
|
{
|
|
BWTEncode m_cBWTEncode = new BWTEncode();
|
|
MTFEncode m_cMTFEncode = new MTFEncode();
|
|
CRangeCoder m_cRangeCoder = new CRangeCoder();
|
|
|
|
const int RANGECODER_BLOCKSIZE = 0x2000;
|
|
|
|
/*
|
|
// Encode
|
|
BOOL Encode( DWORD dwCompressionLevel, BYTE *pDestBuffer, const BYTE *pSrcBuffer, DWORD dwDestLength, DWORD dwSrcLength, DWORD *pdwWritten, HPA_Callback callback, LPVOID pCallbackArg )
|
|
{
|
|
// BWTEncode でサイズが 2 増えるので + 2 する
|
|
BYTE buffer[ RANGECODER_BLOCKSIZE + 2 ];
|
|
DWORD dwSrcCursor = 0, dwDestCursor = 0;
|
|
DWORD written;
|
|
DWORD write_src_size;
|
|
|
|
m_cMTFEncode.InitMTFOrder();
|
|
m_cRangeCoder.InitQSModel();
|
|
while ( dwSrcCursor < dwSrcLength ) {
|
|
if ( callback != NULL ) {
|
|
if ( !callback( pCallbackArg, dwSrcCursor, dwSrcLength ) )
|
|
return FALSE;
|
|
}
|
|
if ( dwDestLength - dwDestCursor <= 4 ) // バッファが足りない
|
|
return FALSE;
|
|
write_src_size = min( RANGECODER_BLOCKSIZE, dwSrcLength - dwSrcCursor );
|
|
m_cBWTEncode.Encode( buffer, pSrcBuffer, write_src_size );
|
|
// BWTEncode でサイズが 2 増えるので + 2 する
|
|
m_cMTFEncode.Encode( buffer, buffer, write_src_size + 2 );
|
|
switch ( dwCompressionLevel ) {
|
|
case CMPL_STORE:
|
|
loc_store_encode:
|
|
written = write_src_size + 2;
|
|
memcpy( pDestBuffer + 4, buffer, written );
|
|
break;
|
|
case CMPL_MAXIMUM:
|
|
if ( !m_cRangeCoder.Encode( pDestBuffer + 4, buffer, dwDestLength - dwDestCursor - 4, write_src_size + 2, &written ) )
|
|
return FALSE;
|
|
// オーバーしたら STORE にする
|
|
if ( written >= write_src_size + 2 ) {
|
|
m_cRangeCoder.InitQSModel();
|
|
goto loc_store_encode;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
written += 4;
|
|
// 書きすぎ
|
|
if ( written > 0xffff || written > dwDestLength - dwDestCursor )
|
|
return FALSE;
|
|
reinterpret_cast< unsigned short * >( pDestBuffer )[0] = static_cast< unsigned short >( written );
|
|
reinterpret_cast< unsigned short * >( pDestBuffer )[1] = static_cast< unsigned short >( write_src_size );
|
|
|
|
pSrcBuffer += write_src_size; dwSrcCursor += write_src_size;
|
|
pDestBuffer += written; dwDestCursor += written;
|
|
}
|
|
*pdwWritten = dwDestCursor;
|
|
if ( callback != NULL )
|
|
if ( !callback( pCallbackArg, dwSrcCursor, dwSrcLength ) )
|
|
return FALSE;
|
|
|
|
return ( dwSrcCursor == dwSrcLength );
|
|
}
|
|
*/
|
|
|
|
// Decode
|
|
public bool Decode (Stream input, Stream output)
|
|
{
|
|
uint dwSrcLength = (uint)input.Length;
|
|
var buffer = new byte [RANGECODER_BLOCKSIZE*4+2];
|
|
uint dwSrcCursor = 0;
|
|
var input_buffer = new byte[RANGECODER_BLOCKSIZE];
|
|
|
|
m_cRangeCoder.InitQSModel();
|
|
m_cMTFEncode.InitMTFOrder();
|
|
|
|
using (var reader = new BinaryReader (input, Encoding.ASCII, true))
|
|
{
|
|
while (dwSrcCursor < dwSrcLength)
|
|
{
|
|
if (dwSrcCursor + 4 >= dwSrcLength)
|
|
return false;
|
|
ushort src_block_size = reader.ReadUInt16();
|
|
ushort dest_block_size = reader.ReadUInt16();
|
|
ushort comp_block_size = (ushort)(src_block_size - 4);
|
|
ushort decomp_block_size = (ushort)(dest_block_size + 2);
|
|
|
|
if (dwSrcCursor + src_block_size > dwSrcLength)
|
|
return false;
|
|
if (src_block_size <= 4 || dest_block_size == 0)
|
|
return false;
|
|
if (comp_block_size == decomp_block_size)
|
|
{
|
|
int read = input.Read (buffer, 0, comp_block_size);
|
|
m_cRangeCoder.InitQSModel();
|
|
}
|
|
else
|
|
{
|
|
if (comp_block_size > input_buffer.Length)
|
|
input_buffer = new byte[comp_block_size];
|
|
int read = input.Read (input_buffer, 0, comp_block_size);
|
|
if (read != comp_block_size)
|
|
return false;
|
|
uint written = m_cRangeCoder.Decode (buffer, input_buffer, decomp_block_size, comp_block_size);
|
|
if (0 == written)
|
|
break;
|
|
if (written != decomp_block_size)
|
|
return false;
|
|
}
|
|
m_cMTFEncode.Decode (buffer, buffer, decomp_block_size);
|
|
m_cBWTEncode.Decode (output, buffer, decomp_block_size);
|
|
|
|
dwSrcCursor += src_block_size;
|
|
}
|
|
}
|
|
return dwSrcCursor == dwSrcLength;
|
|
}
|
|
}
|
|
|
|
internal class CRangeCoder
|
|
{
|
|
byte[] m_pSrcBuffer;
|
|
byte[] m_pDestBuffer;
|
|
uint m_dwSrcLength;
|
|
uint m_dwDestLength;
|
|
uint m_dwSrcIndex;
|
|
uint m_dwDestIndex;
|
|
RangeCoder m_rc = new RangeCoder();
|
|
QSModel m_qsm;
|
|
|
|
const int CODE_BITS = 32;
|
|
const int SHIFT_BITS = CODE_BITS - 9;
|
|
const int EXTRA_BITS = (CODE_BITS - 2) % 8 + 1;
|
|
const uint Top_value = 1u << (CODE_BITS - 1);
|
|
const uint Bottom_value = Top_value >> 8;
|
|
|
|
static readonly int[] RANGECODER_INITFREQ = {
|
|
1400, 640, 320, 240, 160, 120, 80, 64,
|
|
48, 40, 32, 24, 20, 20, 20, 20,
|
|
16, 16, 16, 16, 12, 12, 12, 12,
|
|
12, 12, 8, 8, 8, 8, 8, 8,
|
|
6, 6, 6, 6, 6, 6, 6, 6,
|
|
6, 6, 6, 6, 6, 6, 6, 6,
|
|
5, 5, 5, 5, 5, 5, 5, 5,
|
|
5, 5, 5, 5, 5, 5, 5, 5,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4,
|
|
4, 4, 4, 4, 4, 4, 4, 4,
|
|
4, 4, 4, 4, 4, 4, 4, 4,
|
|
4, 4, 4, 4, 4, 4, 4, 4,
|
|
3, 3, 3, 3, 3, 3, 3, 3,
|
|
3, 3, 3, 3, 3, 3, 3, 3,
|
|
3, 3, 3, 3, 3, 3, 3, 3,
|
|
3, 3, 3, 3, 3, 3, 3, 3,
|
|
|
|
3, 3, 3, 3, 3, 3, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
2, 2, 2, 2, 2, 2, 2, 2,
|
|
|
|
2,
|
|
};
|
|
|
|
public void InitQSModel()
|
|
{
|
|
m_qsm = new QSModel (257, 12, 2000, RANGECODER_INITFREQ, false);
|
|
}
|
|
|
|
/*
|
|
public bool Encode (byte[] dest, const BYTE *src, DWORD destsize, DWORD srcsize, DWORD *pwritten )
|
|
{
|
|
BYTE ch_byte;
|
|
int syfreq, ltfreq;
|
|
|
|
m_dwSrcIndex = m_dwDestIndex = 0;
|
|
m_pSrcBuffer = src;
|
|
m_pDestBuffer = dest;
|
|
m_dwSrcLength = srcsize;
|
|
m_dwDestLength = destsize;
|
|
|
|
//this->InitQSModel();
|
|
this->StartEncoding();
|
|
|
|
while ( m_dwSrcIndex < m_dwSrcLength ) {
|
|
if ( !this->GetSrcByteImpl( &ch_byte ) )
|
|
return false;
|
|
qsgetfreq( &m_qsm, ch_byte, &syfreq, <freq );
|
|
this->EncodeShift( syfreq, ltfreq, 12 );
|
|
qsupdate( &m_qsm, ch_byte );
|
|
}
|
|
qsgetfreq( &m_qsm, 256, &syfreq, <freq );
|
|
this->EncodeShift( syfreq, ltfreq, 12 );
|
|
|
|
this->DoneEncoding();
|
|
*pwritten = m_dwDestIndex; // written-size
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
public uint Decode (byte[] dest, byte[] src, uint destsize, uint srcsize)
|
|
{
|
|
int ch, ltfreq, syfreq;
|
|
|
|
m_dwSrcIndex = m_dwDestIndex = 0;
|
|
m_pSrcBuffer = src;
|
|
m_pDestBuffer = dest;
|
|
m_dwSrcLength = srcsize;
|
|
m_dwDestLength = destsize;
|
|
|
|
StartDecoding();
|
|
|
|
while (m_dwSrcIndex < m_dwSrcLength)
|
|
{
|
|
ltfreq = (int)DecodeCulshift (12);
|
|
ch = m_qsm.GetSym (ltfreq);
|
|
if (256 == ch) // check for end-of-file
|
|
break;
|
|
if (m_dwDestIndex >= m_dwDestLength)
|
|
return 0;
|
|
SetDestByteImpl ((byte)ch);
|
|
m_qsm.GetFreq (ch, out syfreq, out ltfreq);
|
|
DecodeUpdate (syfreq, ltfreq, 1 << 12);
|
|
m_qsm.Update (ch);
|
|
}
|
|
m_qsm.GetFreq (256, out syfreq, out ltfreq);
|
|
DecodeUpdate (syfreq, ltfreq, 1 << 12);
|
|
DoneDecoding();
|
|
return m_dwDestIndex;
|
|
}
|
|
/*
|
|
// Encode --------------------------------------------------------
|
|
void StartEncoding( char c = 0, int initlength = 0 )
|
|
{
|
|
m_rc.low = 0; // Full code range
|
|
m_rc.range = Top_value;
|
|
m_rc.buffer = c;
|
|
m_rc.help = 0; // No bytes to follow
|
|
m_rc.bytecount = initlength;
|
|
}
|
|
void EncNormalize()
|
|
{
|
|
while ( m_rc.range <= Bottom_value ) { // do we need renormalisation?
|
|
if ( m_rc.low < (uint)0xff << SHIFT_BITS ) { // no carry possible --> output
|
|
this->SetDestByteImpl( m_rc.buffer );
|
|
for ( ; m_rc.help; m_rc.help -- )
|
|
this->SetDestByteImpl( 0xff );
|
|
m_rc.buffer = (unsigned char)( m_rc.low >> SHIFT_BITS );
|
|
} else if ( m_rc.low & Top_value ) { // carry now, no future carry
|
|
this->SetDestByteImpl( m_rc.buffer+1 );
|
|
for ( ; m_rc.help; m_rc.help -- )
|
|
this->SetDestByteImpl( 0 );
|
|
m_rc.buffer = (unsigned char)( m_rc.low >> SHIFT_BITS );
|
|
} else // passes on a potential carry
|
|
m_rc.help ++;
|
|
m_rc.range <<= 8;
|
|
m_rc.low = ( m_rc.low << 8 ) & ( Top_value - 1 );
|
|
m_rc.bytecount ++;
|
|
}
|
|
}
|
|
void EncodeFreq (uint sy_f, uint lt_f, uint tot_f)
|
|
{
|
|
uint r, tmp;
|
|
|
|
this->EncNormalize();
|
|
r = m_rc.range / tot_f;
|
|
tmp = r * lt_f;
|
|
m_rc.low += tmp;
|
|
if ( lt_f + sy_f < tot_f )
|
|
m_rc.range = r * sy_f;
|
|
else
|
|
m_rc.range -= tmp;
|
|
}
|
|
void EncodeShift (uint sy_f, uint lt_f, uint shift)
|
|
{
|
|
uint r, tmp;
|
|
|
|
this->EncNormalize();
|
|
r = m_rc.range >> shift;
|
|
tmp = r * lt_f;
|
|
m_rc.low += tmp;
|
|
if ( ( lt_f + sy_f ) >> shift )
|
|
m_rc.range -= tmp;
|
|
else
|
|
m_rc.range = r * sy_f;
|
|
}
|
|
uint4 DoneEncoding()
|
|
{
|
|
uint tmp;
|
|
|
|
this->EncNormalize(); // now we have a normalized state
|
|
m_rc.bytecount += 5;
|
|
if ( ( m_rc.low & ( Bottom_value - 1 ) ) < ( ( m_rc.bytecount & 0xffffffL ) >> 1 ) )
|
|
tmp = m_rc.low >> SHIFT_BITS;
|
|
else
|
|
tmp = ( m_rc.low >> SHIFT_BITS ) + 1;
|
|
if ( tmp > 0xff ) { // we have a carry
|
|
this->SetDestByteImpl( m_rc.buffer + 1 );
|
|
for ( ; m_rc.help; m_rc.help -- )
|
|
this->SetDestByteImpl( 0 );
|
|
} else { // no carry
|
|
this->SetDestByteImpl( m_rc.buffer );
|
|
for ( ; m_rc.help; m_rc.help -- )
|
|
this->SetDestByteImpl( 0xff );
|
|
}
|
|
this->SetDestByteImpl( static_cast< BYTE >( tmp ) );
|
|
this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount >> 16 ) );
|
|
this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount >> 8 ) );
|
|
this->SetDestByteImpl( static_cast< BYTE >( m_rc.bytecount ) );
|
|
|
|
return m_rc.bytecount;
|
|
}
|
|
*/
|
|
|
|
// Decode --------------------------------------------------------
|
|
int StartDecoding ()
|
|
{
|
|
byte c;
|
|
|
|
if (!GetSrcByteImpl (out c))
|
|
return -1;
|
|
if (!GetSrcByteImpl (out m_rc.buffer))
|
|
return -1;
|
|
m_rc.low = (uint)(m_rc.buffer >> (8 - EXTRA_BITS));
|
|
m_rc.range = (uint)1 << EXTRA_BITS;
|
|
|
|
return c;
|
|
}
|
|
|
|
bool DecNormalize()
|
|
{
|
|
while ( m_rc.range <= Bottom_value )
|
|
{
|
|
m_rc.low = ( m_rc.low << 8 ) | (byte)(m_rc.buffer << EXTRA_BITS);
|
|
if (!GetSrcByteImpl (out m_rc.buffer))
|
|
return false;
|
|
m_rc.low |= (uint)m_rc.buffer >> ( 8 - EXTRA_BITS );
|
|
m_rc.range <<= 8;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint DecodeCulshift (int shift)
|
|
{
|
|
uint tmp;
|
|
|
|
DecNormalize();
|
|
m_rc.help = m_rc.range >> shift;
|
|
tmp = m_rc.low / m_rc.help;
|
|
return (0 != (tmp >> shift) ? (1u << shift) - 1u : tmp);
|
|
}
|
|
|
|
void DecodeUpdate (int sy_f, int lt_f, int tot_f)
|
|
{
|
|
uint tmp = m_rc.help * (uint)lt_f;
|
|
|
|
m_rc.low -= tmp;
|
|
if ( lt_f + sy_f < tot_f )
|
|
m_rc.range = m_rc.help * (uint)sy_f;
|
|
else
|
|
m_rc.range -= tmp;
|
|
}
|
|
|
|
void DoneDecoding()
|
|
{
|
|
DecNormalize(); // normalize to use up all bytes
|
|
}
|
|
|
|
// I/O -----------------------------------------------------------
|
|
bool GetSrcByteImpl (out byte pData)
|
|
{
|
|
if (m_dwSrcIndex >= m_dwSrcLength)
|
|
{
|
|
pData = 0;
|
|
return false;
|
|
}
|
|
pData = m_pSrcBuffer[m_dwSrcIndex++];
|
|
return true;
|
|
}
|
|
|
|
bool SetDestByteImpl (byte byData)
|
|
{
|
|
if (m_dwDestIndex >= m_dwDestLength)
|
|
return false;
|
|
m_pDestBuffer[m_dwDestIndex++] = byData;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal class RangeCoder
|
|
{
|
|
public uint low; /* low end of interval */
|
|
public uint range; /* length of interval */
|
|
public uint help; /* bytes_to_follow resp. intermediate value */
|
|
public byte buffer; /* buffer for input/output */
|
|
/* the following is used only when encoding */
|
|
public uint bytecount; /* counter for outputed bytes */
|
|
}
|
|
|
|
// とりあえずこれで可逆性を概ね確認 (数タイトルの song.txt で確認)
|
|
internal class BWTEncode
|
|
{
|
|
public const ulong BWT_SORTTABLESIZE = 0x00010000;
|
|
|
|
/*
|
|
byte[] m_pWorkTable = new byte[BWT_SORTTABLESIZE / 2];
|
|
|
|
void Encode( BYTE *dest, const BYTE *src, int size )
|
|
{
|
|
int top = 0; // 初期値は不要だが、警告回避のため
|
|
BYTE *ptr;
|
|
int count[256] = { 0, };
|
|
int count_sum[256+1];
|
|
BYTE *sort_buffer = new BYTE[size*2];
|
|
LPBYTE *sort_table = new LPBYTE[BWT_SORTTABLESIZE];
|
|
|
|
// 作業領域にコピー
|
|
memcpy( sort_buffer, src, size );
|
|
memcpy( sort_buffer + size, sort_buffer, size );
|
|
|
|
// 分布数え上げソート
|
|
for ( int i = 0; i < size; i ++ )
|
|
count[ sort_buffer[i] ]++;
|
|
count_sum[0] = 0;
|
|
for ( int i = 1; i <= 256; i ++ )
|
|
count_sum[i] = count[i-1] + count_sum[i-1];
|
|
for ( int i = 1; i < 256; i ++ )
|
|
count[i] += count[i-1];
|
|
|
|
for ( int i = size - 1; i >= 0; i -- ) {
|
|
ptr = sort_buffer + i;
|
|
sort_table[ -- count[*ptr] ] = ptr;
|
|
}
|
|
|
|
// 2 段階ソート
|
|
for ( int i = 1; i < 256; i ++ ) {
|
|
int j, k;
|
|
int high = count_sum[i+1];
|
|
|
|
for ( j = k = count_sum[i]; j < high; j ++ ) {
|
|
ptr = sort_table[j];
|
|
if ( *ptr > *(ptr + 1) ) {
|
|
sort_table[j] = sort_table[k];
|
|
sort_table[k ++] = ptr;
|
|
}
|
|
}
|
|
if ( high - k > 1 )
|
|
this->MergeSort( sort_table, k, high - 1, size );
|
|
}
|
|
// 0 は全てソート
|
|
if ( count_sum[1] > 1 )
|
|
this->MergeSort( sort_table, 0, count_sum[1] - 1, size );
|
|
// ソート不要部分
|
|
for ( int i = 0; i < size; i ++ ) {
|
|
ptr = sort_table[i];
|
|
if ( ptr == sort_buffer )
|
|
ptr += size;
|
|
if ( *(ptr - 1) > *ptr )
|
|
sort_table[ count_sum[*(ptr - 1)] ++ ] = ptr - 1;
|
|
}
|
|
// 出力
|
|
for ( int i = 0; i < size; i ++ ) {
|
|
ptr = sort_table[i];
|
|
if ( ptr == sort_buffer )
|
|
top = i;
|
|
dest[i+2] = *(ptr + size - 1);
|
|
}
|
|
*reinterpret_cast< unsigned short * >( dest ) = static_cast< unsigned short >( top );
|
|
// 解放
|
|
delete[] sort_buffer;
|
|
delete[] sort_table;
|
|
}
|
|
*/
|
|
int[] sort_table = new int[BWT_SORTTABLESIZE];
|
|
|
|
public void Decode (Stream dest, byte[] src, int size)
|
|
{
|
|
int[] count = new int[256];
|
|
int top = src[0] | src[1] << 8;
|
|
|
|
int pos = 2;
|
|
size -= 2;
|
|
// 分布数え上げソート
|
|
for (int i = 0; i < size; i++)
|
|
count[ src[pos+i] ]++;
|
|
for (short i = 1; i < 256; i++)
|
|
count[i] += count[i-1];
|
|
for (int i = size - 1; i >= 0; i --)
|
|
{
|
|
sort_table[--count[src[pos+i]]] = i;
|
|
}
|
|
// 出力
|
|
int ptr = sort_table[top];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
dest.WriteByte (src[pos+ptr]);
|
|
ptr = sort_table[ptr];
|
|
}
|
|
}
|
|
/*
|
|
void MergeSort( BYTE *sort_table[], int low, int high, int size )
|
|
{
|
|
int len = size - 1;
|
|
|
|
if ( high - low <= 10 ) {
|
|
this->InsertSort( sort_table, low, high, size );
|
|
} else {
|
|
int middle = (low + high) / 2;
|
|
int i, p, j, k;
|
|
|
|
this->MergeSort( sort_table, low, middle, size );
|
|
this->MergeSort( sort_table, middle + 1, high, size );
|
|
p = 0;
|
|
i = low;
|
|
while ( i <= middle )
|
|
m_pWorkTable[p ++] = sort_table[i ++];
|
|
i = middle + 1;
|
|
j = 0;
|
|
k = low;
|
|
while ( i <= high && j < p ) {
|
|
if ( memcmp( m_pWorkTable[j] + 1, sort_table[i] + 1, len ) <= 0 )
|
|
sort_table[k ++] = m_pWorkTable[j ++];
|
|
else
|
|
sort_table[k ++] = sort_table[i ++];
|
|
}
|
|
while ( j < p )
|
|
sort_table[k ++] = m_pWorkTable[j ++];
|
|
}
|
|
}
|
|
void InsertSort( BYTE *sort_table[], int low, int high, int size )
|
|
{
|
|
int j, len = size - 1;
|
|
|
|
for ( int i = low + 1; i <= high ; i ++ ) {
|
|
BYTE *tmp = sort_table[i];
|
|
|
|
for ( j = i - 1; j >= low && memcmp( tmp + 1, sort_table[j] + 1, len ) < 0; j -- )
|
|
sort_table[j + 1] = sort_table[j];
|
|
sort_table[j + 1] = tmp;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
internal class MTFEncode
|
|
{
|
|
byte[] m_MTFTable = new byte[256];
|
|
|
|
public void InitMTFOrder()
|
|
{
|
|
for (int i = 0; i < 256; i++)
|
|
m_MTFTable[i] = (byte)i;
|
|
}
|
|
|
|
// MTF は当然、destsize == srcsize
|
|
public void Encode (byte[] dest, byte[] src, int size)
|
|
{
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
byte c = src[i];
|
|
byte n = 0;
|
|
|
|
while (m_MTFTable[n] != c)
|
|
n++;
|
|
if (n > 0)
|
|
{
|
|
Buffer.BlockCopy (m_MTFTable, 0, m_MTFTable, 1, n);
|
|
m_MTFTable[0] = c;
|
|
}
|
|
dest[i] = n;
|
|
}
|
|
}
|
|
|
|
// MTF は当然、destsize == srcsize
|
|
public void Decode (byte[] dest, byte[] src, int size)
|
|
{
|
|
for ( int i = 0; i < size; i++ )
|
|
{
|
|
byte n = src[i];
|
|
byte c = m_MTFTable[n];
|
|
if (n > 0)
|
|
{
|
|
Buffer.BlockCopy (m_MTFTable, 0, m_MTFTable, 1, n);
|
|
m_MTFTable[0] = c;
|
|
}
|
|
dest[i] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Quasistatic probability model
|
|
|
|
// 若干改変 by juicy.gt at 2008/03/27 00:00
|
|
|
|
(c) Michael Schindler
|
|
1997, 1998, 2000
|
|
http://www.compressconsult.com/
|
|
michael@compressconsult.com
|
|
|
|
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 of the License, 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. It may be that this
|
|
program violates local patents in your country, however it is
|
|
belived (NO WARRANTY!) to be patent-free here in Austria.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
MA 02111-1307, USA.
|
|
|
|
Qsmodel is a quasistatic probability model that periodically
|
|
(at chooseable intervals) updates probabilities of symbols;
|
|
it also allows to initialize probabilities. Updating is done more
|
|
frequent in the beginning, so it adapts very fast even without
|
|
initialisation.
|
|
|
|
it provides function for creation, deletion, query for probabilities
|
|
and symbols and model updating.
|
|
*/
|
|
|
|
internal class QSModel
|
|
{
|
|
public int m_n; /* number of symbols */
|
|
public int m_left; /* symbols to next rescale */
|
|
public int m_nextleft; /* symbols with other increment */
|
|
public int m_rescale; /* intervals between rescales */
|
|
public int m_targetrescale; /* should be interval between rescales */
|
|
public int m_incr; /* increment per update */
|
|
public int m_searchshift; /* shift for lt_freq before using as index */
|
|
public ushort[] m_cf; /* array of cumulative frequencies */
|
|
public ushort[] m_newf; /* array for collecting ststistics */
|
|
public ushort[] m_search; /* structure for searching on decompression */
|
|
|
|
public const int TBLSHIFT = 7;
|
|
|
|
/// <summary>
|
|
/// initialisation of qsmodel
|
|
/// </summary>
|
|
/// <param name="n">number of symbols in that model</param>
|
|
/// <param name="lg_totf">base2 log of total frequency count</param>
|
|
/// <param name="rescale">desired rescaling interval, should be < 1<<(lg_totf+1)</param>
|
|
/// <param name="init">array of int's to be used for initialisation (NULL ok)</param>
|
|
/// <param name="compress">true on compression, false on decompression</param>
|
|
public QSModel (int n, int lg_totf, int rescale, int[] init, bool compress)
|
|
{
|
|
m_n = n;
|
|
m_targetrescale = rescale;
|
|
m_searchshift = lg_totf - TBLSHIFT;
|
|
if (m_searchshift < 0)
|
|
m_searchshift = 0;
|
|
m_cf = new ushort[n+1];
|
|
m_newf = new ushort[n+1];
|
|
m_cf[n] = (ushort)(1 << lg_totf);
|
|
m_cf[0] = 0;
|
|
if (compress)
|
|
{
|
|
m_search = null;
|
|
}
|
|
else
|
|
{
|
|
m_search = new ushort[(1<<TBLSHIFT)+1];
|
|
m_search[1<<TBLSHIFT] = (ushort)(n-1);
|
|
}
|
|
Reset (init);
|
|
}
|
|
|
|
/// <summary>
|
|
/// reinitialisation of qsmodel
|
|
/// </summary>
|
|
/// <param name="init">array of int's to be used for initialisation (NULL ok)</param>
|
|
public void Reset (int[] init)
|
|
{
|
|
int i;
|
|
m_rescale = m_n>>4 | 2;
|
|
m_nextleft = 0;
|
|
if (init == null)
|
|
{
|
|
int initval = m_cf[m_n] / m_n;
|
|
int end = m_cf[m_n] % m_n;
|
|
for (i = 0; i < end; i++)
|
|
m_newf[i] = (ushort)(initval+1);
|
|
for (; i < m_n; i++)
|
|
m_newf[i] = (ushort)initval;
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < m_n; i++)
|
|
m_newf[i] = (ushort)init[i];
|
|
}
|
|
DoRescale();
|
|
}
|
|
|
|
void DoRescale ()
|
|
{
|
|
if (0 != m_nextleft) /* we have some more before actual rescaling */
|
|
{
|
|
m_incr++;
|
|
m_left = m_nextleft;
|
|
m_nextleft = 0;
|
|
return;
|
|
}
|
|
if (m_rescale < m_targetrescale) /* double rescale interval if needed */
|
|
{
|
|
m_rescale <<= 1;
|
|
if (m_rescale > m_targetrescale)
|
|
m_rescale = m_targetrescale;
|
|
}
|
|
int i, cf, missing;
|
|
cf = missing = m_cf[m_n]; /* do actual rescaling */
|
|
for (i = m_n-1; i != 0; i--)
|
|
{
|
|
int tmp = m_newf[i];
|
|
cf -= tmp;
|
|
m_cf[i] = (ushort)cf;
|
|
tmp = tmp>>1 | 1;
|
|
missing -= tmp;
|
|
m_newf[i] = (ushort)tmp;
|
|
}
|
|
if (cf != m_newf[0])
|
|
throw new ApplicationException ("Run-time error in QSModel.DoRescale");
|
|
|
|
m_newf[0] = (ushort)(m_newf[0]>>1 | 1);
|
|
missing -= m_newf[0];
|
|
m_incr = missing / m_rescale;
|
|
m_nextleft = missing % m_rescale;
|
|
m_left = m_rescale - m_nextleft;
|
|
if (m_search != null)
|
|
{
|
|
i = m_n;
|
|
while (i != 0)
|
|
{
|
|
int end = (m_cf[i]-1) >> m_searchshift;
|
|
i--;
|
|
int start = m_cf[i] >> m_searchshift;
|
|
while (start <= end)
|
|
{
|
|
m_search[start] = (ushort)i;
|
|
start++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// retrieval of estimated frequencies for a symbol
|
|
/// </summary>
|
|
/// <param name="sym">symbol for which data is desired; must be <n</param>
|
|
/// <param name="sy_f">frequency of that symbol</param>
|
|
/// <param name="lt_f">frequency of all smaller symbols together</param>
|
|
/// the total frequency is 1<<lg_totf
|
|
public void GetFreq (int sym, out int sy_f, out int lt_f)
|
|
{
|
|
lt_f = m_cf[sym];
|
|
sy_f = m_cf[sym+1] - lt_f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// find out symbol for a given cumulative frequency.
|
|
/// </summary>
|
|
/// <param name="lt_f">cumulative frequency</param>
|
|
public int GetSym (int lt_f)
|
|
{
|
|
int lo, hi;
|
|
int tmp = lt_f >> m_searchshift;
|
|
lo = m_search[tmp];
|
|
hi = m_search[tmp+1] + 1;
|
|
while (lo+1 < hi)
|
|
{
|
|
int mid = (lo + hi) >> 1;
|
|
if (lt_f < m_cf[mid])
|
|
hi = mid;
|
|
else
|
|
lo = mid;
|
|
}
|
|
return lo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// update model
|
|
/// </summary>
|
|
/// <param name="sym">symbol that occurred (must be <n from init)</param>
|
|
public void Update (int sym)
|
|
{
|
|
if (m_left <= 0)
|
|
DoRescale();
|
|
m_left--;
|
|
m_newf[sym] += (ushort)m_incr;
|
|
}
|
|
}
|
|
}
|