Constants

This commit is contained in:
Matt Nadareski
2022-05-24 15:52:05 -07:00
parent 1a0b83a9f1
commit 4dbaa415c5
14 changed files with 1241 additions and 1125 deletions

View File

@@ -19,6 +19,7 @@ using System.IO;
using System.Text;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.CHM
{
@@ -242,7 +243,7 @@ namespace LibMSPackSharp.CHM
return Error = err;
// Validate reset_interval
if (lzxControlData.ResetInterval == 0 || (lzxControlData.ResetInterval % LZX.LZX_FRAME_SIZE) != 0)
if (lzxControlData.ResetInterval == 0 || (lzxControlData.ResetInterval % LZX_FRAME_SIZE) != 0)
{
Console.WriteLine("Bad controldata reset interval");
return Error = Error.MSPACK_ERR_DATAFORMAT;
@@ -252,7 +253,7 @@ namespace LibMSPackSharp.CHM
int entry = (int)(file.Offset / lzxControlData.ResetInterval);
// Convert from reset interval multiple (usually 64k) to 32k frames
entry *= (int)lzxControlData.ResetInterval / LZX.LZX_FRAME_SIZE;
entry *= (int)lzxControlData.ResetInterval / LZX_FRAME_SIZE;
// Read the reset table entry
if (ReadResetTable(sec, (uint)entry, out long length, out long offset))
@@ -284,11 +285,11 @@ namespace LibMSPackSharp.CHM
State.InOffset = file.Section.Header.Sec0.Offset + sec.Content.Offset + offset;
// Set start offset and overall remaining stream length
State.Offset = entry * LZX.LZX_FRAME_SIZE;
State.Offset = entry * LZX_FRAME_SIZE;
length -= State.Offset;
// Initialise LZX stream
State.State = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, windowBits, (int)lzxControlData.ResetInterval / LZX.LZX_FRAME_SIZE, 4096, length, false);
State.State = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, windowBits, (int)lzxControlData.ResetInterval / LZX_FRAME_SIZE, 4096, length, false);
if (State.State == null)
Error = Error.MSPACK_ERR_NOMEMORY;
@@ -1081,7 +1082,7 @@ namespace LibMSPackSharp.CHM
return false;
// Check sanity of reset table
if (lzxResetTable.FrameLength != LZX.LZX_FRAME_SIZE)
if (lzxResetTable.FrameLength != LZX_FRAME_SIZE)
{
Console.WriteLine("Bad reset table frame length");
return false;

View File

@@ -8,7 +8,7 @@
*/
using System;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.CHM
{
@@ -94,8 +94,8 @@ namespace LibMSPackSharp.CHM
controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010);
break;
case 2:
controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C) * LZX.LZX_FRAME_SIZE;
controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010) * LZX.LZX_FRAME_SIZE;
controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C) * LZX_FRAME_SIZE;
controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010) * LZX_FRAME_SIZE;
break;
default:
return Error.MSPACK_ERR_DATAFORMAT;

View File

@@ -0,0 +1,78 @@
/* This file is part of libmspack.
* (C) 2003-2013 Stuart Caie.
*
* The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation.
*
* libmspack is free software { get; set; } you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.Compression
{
public class BufferState
{
/// <summary>
/// i_ptr
/// </summary>
public int InputPointer { get; set; }
/// <summary>
/// i_end
/// </summary>
public int InputEnd { get; set; }
/// <summary>
/// bit_buffer
/// </summary>
public uint BitBuffer { get; set; }
/// <summary>
/// bits_left
/// </summary>
public int BitsLeft { get; set; }
#region Common
/// <summary>
/// Initialises bitstream state in state structure
/// </summary>
public void Init()
{
InputPointer = 0;
InputEnd = 0;
BitBuffer = 0;
BitsLeft = 0;
}
#endregion
#region MSB
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_MSB(int nbits)
{
BitBuffer <<= nbits;
BitsLeft -= nbits;
}
#endregion
#region LSB
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_LSB(int nbits)
{
BitBuffer >>= nbits;
BitsLeft -= nbits;
}
#endregion
}
}

View File

@@ -17,42 +17,17 @@ namespace LibMSPackSharp.Compression
{
public abstract class CompressionStream : BaseDecompressState
{
#region Constants
/// <summary>
/// Number of bits in a character
/// </summary>
private const int CHAR_BIT = 8;
/// <summary>
/// Bit width of a UInt32 bit buffer
/// </summary>
public const int BITBUF_WIDTH = 4 * CHAR_BIT;
/// <summary>
/// Maximum bits in a Huffman code
/// </summary>
public const int HUFF_MAXBITS = 16;
#endregion
#region I/O buffering
public byte[] InputBuffer { get; set; }
public uint InputBufferSize { get; set; }
public int InputPointer { get; set; }
public int InputEnd { get; set; }
public int OutputPointer { get; set; }
public int OutputEnd { get; set; }
public uint BitBuffer { get; set; }
public int BitsLeft { get; set; }
internal BufferState BufferState { get; set; }
/// <summary>
/// Have we reached the end of input?
@@ -137,43 +112,44 @@ namespace LibMSPackSharp.Compression
/// </summary>
public void INIT_BITS()
{
InputPointer = 0;
InputEnd = 0;
BitBuffer = 0;
BitsLeft = 0;
BufferState = new BufferState();
BufferState.Init();
EndOfInput = 0;
}
/// <summary>
/// Stores bitstream state in state structure
/// </summary>
public void STORE_BITS(int i_ptr, int i_end, uint bit_buffer, int bits_left)
public void STORE_BITS(BufferState state)
{
InputPointer = i_ptr;
InputEnd = i_end;
BitBuffer = bit_buffer;
BitsLeft = bits_left;
BufferState.InputPointer = state.InputPointer;
BufferState.InputEnd = state.InputEnd;
BufferState.BitBuffer = state.BitBuffer;
BufferState.BitsLeft = state.BitsLeft;
}
/// <summary>
/// Restores bitstream state from state structure
/// </summary>
public void RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left)
public BufferState RESTORE_BITS()
{
i_ptr = InputPointer;
i_end = InputEnd;
bit_buffer = BitBuffer;
bits_left = BitsLeft;
return new BufferState()
{
InputPointer = BufferState.InputPointer,
InputEnd = BufferState.InputEnd,
BitBuffer = BufferState.BitBuffer,
BitsLeft = BufferState.BitsLeft,
};
}
/// <summary>
/// Ensure there are at least N bits in the bit buffer
/// </summary>
public void ENSURE_BITS(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public void ENSURE_BITS(int nbits, BufferState state)
{
while (bits_left < nbits)
while (state.BitsLeft < nbits)
{
READ_BYTES(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
READ_BYTES(state);
if (Error != Error.MSPACK_ERR_OK)
return;
}
@@ -182,23 +158,23 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Read from the input if the buffer is empty
/// </summary>
public void READ_IF_NEEDED(ref int i_ptr, ref int i_end)
public void READ_IF_NEEDED(BufferState state)
{
if (i_ptr >= i_end)
if (state.InputPointer >= state.InputEnd)
{
ReadInput();
if (Error != Error.MSPACK_ERR_OK)
return;
i_ptr = InputPointer;
i_end = InputEnd;
state.InputPointer = BufferState.InputPointer;
state.InputEnd = BufferState.InputEnd;
}
}
/// <summary>
/// Read bytes from the input into the bit buffer
/// </summary>
public abstract void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left);
public abstract void READ_BYTES(BufferState state);
/// <summary>
/// Read an input stream and fill the buffer
@@ -231,8 +207,8 @@ namespace LibMSPackSharp.Compression
}
// Update i_ptr and i_end
InputPointer = 0;
InputEnd = read;
BufferState.InputPointer = 0;
BufferState.InputEnd = read;
}
#endregion
@@ -242,10 +218,10 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Inject data into the bit buffer
/// </summary>
public void INJECT_BITS_MSB(int bitdata, int nbits, ref uint bit_buffer, ref int bits_left)
public void INJECT_BITS_MSB(int bitdata, int nbits, BufferState state)
{
bit_buffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - bits_left));
bits_left += nbits;
state.BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - state.BitsLeft));
state.BitsLeft += nbits;
}
/// <summary>
@@ -256,52 +232,43 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Takes N bits from the buffer and puts them in var
/// </summary>
public long READ_BITS_MSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_BITS_MSB(int nbits, BufferState state)
{
ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
ENSURE_BITS(nbits, state);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_MSB(nbits, bit_buffer);
long temp = PEEK_BITS_MSB(nbits, state.BitBuffer);
REMOVE_BITS_MSB(nbits, ref bit_buffer, ref bits_left);
state.REMOVE_BITS_MSB(nbits);
return temp;
}
/// <summary>
/// Read multiple bits and put them in var
/// </summary>
public long READ_MANY_BITS_MSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_MANY_BITS_MSB(int nbits, BufferState state)
{
byte needed = (byte)(nbits), bitrun;
long temp = 0;
while (needed > 0)
{
if (bits_left <= (BITBUF_WIDTH - 16))
if (state.BitsLeft <= (BITBUF_WIDTH - 16))
{
READ_BYTES(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
READ_BYTES(state);
if (Error != Error.MSPACK_ERR_OK)
return -1;
}
bitrun = (byte)((bits_left < needed) ? bits_left : needed);
temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun, bit_buffer);
REMOVE_BITS_MSB(bitrun, ref bit_buffer, ref bits_left);
bitrun = (byte)((state.BitsLeft < needed) ? state.BitsLeft : needed);
temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun, state.BitBuffer);
state.REMOVE_BITS_MSB(bitrun);
needed -= bitrun;
}
return temp;
}
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_MSB(int nbits, ref uint bit_buffer, ref int bits_left)
{
bit_buffer <<= nbits;
bits_left -= nbits;
}
#endregion
#region LSB
@@ -309,10 +276,10 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Inject data into the bit buffer
/// </summary>
public void INJECT_BITS_LSB(int bitdata, int nbits, ref uint bit_buffer, ref int bits_left)
public void INJECT_BITS_LSB(int bitdata, int nbits, BufferState state)
{
bit_buffer |= (uint)(bitdata << bits_left);
bits_left += nbits;
state.BitBuffer |= (uint)(bitdata << state.BitsLeft);
state.BitsLeft += nbits;
}
/// <summary>
@@ -323,47 +290,38 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Extracts without removing N bits from the bit buffer using a bit mask
/// </summary>
public long PEEK_BITS_T_LSB(int nbits, uint bit_buffer) => bit_buffer & lsb_bit_mask[(nbits)];
public long PEEK_BITS_T_LSB(int nbits, uint bit_buffer) => bit_buffer & LSBBitMask[(nbits)];
/// <summary>
/// Takes N bits from the buffer and puts them in var
/// </summary>
public long READ_BITS_LSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_BITS_LSB(int nbits, BufferState state)
{
ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
ENSURE_BITS(nbits, state);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_LSB(nbits, bit_buffer);
long temp = PEEK_BITS_LSB(nbits, state.BitBuffer);
REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left);
state.REMOVE_BITS_LSB(nbits);
return temp;
}
/// <summary>
/// Takes N bits from the buffer and puts them in var using a bit mask
/// </summary>
public long READ_BITS_T_LSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_BITS_T_LSB(int nbits, BufferState state)
{
ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
ENSURE_BITS(nbits, state);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_T_LSB(nbits, bit_buffer);
long temp = PEEK_BITS_T_LSB(nbits, state.BitBuffer);
REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left);
state.REMOVE_BITS_LSB(nbits);
return temp;
}
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_LSB(int nbits, ref uint bit_buffer, ref int bits_left)
{
bit_buffer >>= nbits;
bits_left -= nbits;
}
#endregion
#endregion
@@ -385,14 +343,14 @@ namespace LibMSPackSharp.Compression
/// Decodes the next huffman symbol from the input bitstream into var.
/// Do not use this macro on a table unless build_decode_table() succeeded.
/// </summary>
public long READ_HUFFSYM_MSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_HUFFSYM_MSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
{
ENSURE_BITS(HUFF_MAXBITS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
ushort sym = table[PEEK_BITS_MSB(tablebits, bit_buffer)];
ENSURE_BITS(HUFF_MAXBITS, state);
ushort sym = table[PEEK_BITS_MSB(tablebits, state.BitBuffer)];
if (sym >= maxsymbols)
HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols, bit_buffer);
HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols, state.BitBuffer);
REMOVE_BITS_MSB(lengths[sym], ref bit_buffer, ref bits_left);
state.REMOVE_BITS_MSB(lengths[sym]);
return sym;
}
@@ -523,14 +481,14 @@ namespace LibMSPackSharp.Compression
/// Decodes the next huffman symbol from the input bitstream into var.
/// Do not use this macro on a table unless build_decode_table() succeeded.
/// </summary>
public long READ_HUFFSYM_LSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_HUFFSYM_LSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
{
ENSURE_BITS(HUFF_MAXBITS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
ushort sym = table[PEEK_BITS_LSB(tablebits, bit_buffer)];
ENSURE_BITS(HUFF_MAXBITS, state);
ushort sym = table[PEEK_BITS_LSB(tablebits, state.BitBuffer)];
if (sym >= maxsymbols)
HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols, bit_buffer);
HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols, state.BitBuffer);
REMOVE_BITS_LSB(lengths[sym], ref bit_buffer, ref bits_left);
state.REMOVE_BITS_LSB(lengths[sym]);
return sym;
}

View File

@@ -11,11 +11,282 @@ namespace LibMSPackSharp.Compression
{
internal static class Constants
{
/* lsb_bit_mask[n] = (1 << n) - 1 */
internal static readonly ushort[] lsb_bit_mask = new ushort[17]
#region readbits.h
/// <summary>
/// Bit width of a UInt32 bit buffer
/// </summary>
public const int BITBUF_WIDTH = 4 * CHAR_BIT;
/// <summary>
/// Number of bits in a character
/// </summary>
internal const int CHAR_BIT = 8;
// lsb_bit_mask[n] = (1 << n) - 1
internal static readonly ushort[] LSBBitMask = new ushort[17]
{
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#endregion
#region readhuff.h
/// <summary>
/// Maximum bits in a Huffman code
/// </summary>
public const int HUFF_MAXBITS = 16;
#endregion
#region LZSS
/// <summary>
/// Size of an LZSS window
/// </summary>
public const int LZSS_WINDOW_SIZE = 4096;
/// <summary>
/// LZSS window fill byte
/// </summary>
public const byte LZSS_WINDOW_FILL = 0x20;
#endregion
#region LZX
// Some constants defined by the LZX specification
public const int LZX_MIN_MATCH = 2;
public const int LZX_MAX_MATCH = 257;
public const int LZX_NUM_CHARS = 256;
public const int LZX_PRETREE_NUM_ELEMENTS = 20;
public const int LZX_ALIGNED_NUM_ELEMENTS = 8; // Aligned offset tree #elements
public const int LZX_NUM_PRIMARY_LENGTHS = 7; // This one missing from spec!
public const int LZX_NUM_SECONDARY_LENGTHS = 249; // Length tree #elements
// LZX huffman defines: tweak tablebits as desired
public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
public const byte LZX_PRETREE_TABLEBITS = 6;
public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8;
public const byte LZX_MAINTREE_TABLEBITS = 12;
public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1;
public const byte LZX_LENGTH_TABLEBITS = 12;
public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
public const byte LZX_ALIGNED_TABLEBITS = 7;
public const int LZX_LENTABLE_SAFETY = 64; // Table decoding overruns are allowed
public const int LZX_FRAME_SIZE = 32768; // The size of a frame in LZX
#region LZX static data tables
/* LZX static data tables:
*
* LZX uses 'position slots' to represent match offsets. For every match,
* a small 'position slot' number and a small offset from that slot are
* encoded instead of one large offset.
*
* The number of slots is decided by how many are needed to encode the
* largest offset for a given window size. This is easy when the gap between
* slots is less than 128Kb, it's a linear relationship. But when extra_bits
* reaches its limit of 17 (because LZX can only ensure reading 17 bits of
* data at a time), we can only jump 128Kb at a time and have to start
* using more and more position slots as each window size doubles.
*
* position_base[] is an index to the position slot bases
*
* extra_bits[] states how many bits of offset-from-base data is needed.
*
* They are calculated as follows:
* extra_bits[i] = 0 where i < 4
* extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36
* extra_bits[i] = 17 where i >= 36
* position_base[0] = 0
* position_base[i] = position_base[i-1] + (1 << extra_bits[i-1])
*/
public static readonly uint[] LZXPositionSlots = new uint[11]
{
30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
};
public static readonly byte[] LZXExtraBits = new byte[36]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16
};
public static readonly uint[] LZXPositionBase = new uint[290]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512,
768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768,
49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360,
786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512,
2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088,
3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664,
4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240,
6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816,
7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392,
8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968,
9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544,
10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048,
11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552,
12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056,
12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560,
13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064,
14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568,
15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072,
16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576,
17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080,
18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584,
19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088,
20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592,
21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096,
22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600,
23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104,
23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608,
24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112,
25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616,
26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120,
27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624,
28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128,
29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632,
30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136,
31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640,
32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144,
33161216, 33292288, 33423360
};
#endregion
#endregion
#region MSZIP
public const int MSZIP_FRAME_SIZE = 32768; // Size of LZ history window
public const int MSZIP_LITERAL_MAXSYMBOLS = 288; // literal/length huffman tree
public const int MSZIP_LITERAL_TABLEBITS = 9;
public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; // Distance huffman tree
public const int MSZIP_DISTANCE_TABLEBITS = 6;
// If there are less direct lookup entries than symbols, the longer
// code pointers will be <= maxsymbols. This must not happen, or we
// will decode entries badly
//public const int MSZIP_LITERAL_TABLESIZE = (MSZIP_LITERAL_MAXSYMBOLS * 4);
public const int MSZIP_LITERAL_TABLESIZE = ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2));
//public const int MSZIP_DISTANCE_TABLESIZE = (MSZIP_DISTANCE_MAXSYMBOLS * 4);
public const int MSZIP_DISTANCE_TABLESIZE = ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2));
/// <summary>
/// Match lengths for literal codes 257.. 285
/// </summary>
public static readonly ushort[] LiteralLengths = new ushort[29]
{
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
/// <summary>
/// Match offsets for distance codes 0 .. 29
/// </summary>
public static readonly ushort[] DistanceOffsets = new ushort[30]
{
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
/// <summary>
/// Extra bits required for literal codes 257.. 285
/// </summary>
public static readonly byte[] LiteralExtraBits = new byte[29]
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
/// <summary>
/// Extra bits required for distance codes 0 .. 29
/// </summary>
public static readonly byte[] DistanceExtraBits = new byte[30]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
/// <summary>
/// The order of the bit length Huffman code lengths
/// </summary>
public static readonly byte[] BitLengthOrder = new byte[19]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
#endregion
#region QTM
public const int QTM_FRAME_SIZE = 32768;
/* Quantum static data tables:
*
* Quantum uses 'position slots' to represent match offsets. For every
* match, a small 'position slot' number and a small offset from that slot
* are encoded instead of one large offset.
*
* position_base[] is an index to the position slot bases
*
* extra_bits[] states how many bits of offset-from-base data is needed.
*
* length_base[] and length_extra[] are equivalent in function, but are
* used for encoding selector 6 (variable length match) match lengths,
* instead of match offsets.
*
* They are generated with the following code:
* uint i, offset;
* for (i = 0, offset = 0; i < 42; i++) {
* position_base[i] = offset;
* extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1;
* offset += 1 << extra_bits[i];
* }
* for (i = 0, offset = 0; i < 26; i++) {
* length_base[i] = offset;
* length_extra[i] = (i < 2 ? 0 : i - 2) >> 2;
* offset += 1 << length_extra[i];
* }
* length_base[26] = 254; length_extra[26] = 0;
*/
public static readonly uint[] QTMPositionBase = new uint[42]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864
};
public static readonly byte[] QTMExtraBits = new byte[42]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19
};
public static readonly byte[] QTMLengthBase = new byte[27]
{
0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26,
30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254
};
public static readonly byte[] QTMLengthExtra = new byte[27]
{
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
#endregion
}
}

View File

@@ -9,6 +9,7 @@
using System.IO;
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
@@ -47,39 +48,39 @@ namespace LibMSPackSharp.Compression
// Reset global state
lzh.INIT_BITS();
lzh.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
BufferState state = lzh.RESTORE_BITS();
for (i = 0; i < LZSS.LZSS_WINDOW_SIZE; i++)
for (i = 0; i < LZSS_WINDOW_SIZE; i++)
{
lzh.Window[i] = LZSS.LZSS_WINDOW_FILL;
lzh.Window[i] = LZSS_WINDOW_FILL;
}
// Read 6 encoding types (for byte alignment) but only 5 are needed
for (i = 0; i < 6; i++)
{
types[i] = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
types[i] = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
// Read huffman table symbol lengths and build huffman trees
BUILD_TREE(lzh, types[0], lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
BUILD_TREE(lzh, types[1], lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
BUILD_TREE(lzh, types[2], lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
BUILD_TREE(lzh, types[3], lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
BUILD_TREE(lzh, types[4], lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
BUILD_TREE(lzh, types[0], lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS, state);
BUILD_TREE(lzh, types[1], lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS, state);
BUILD_TREE(lzh, types[2], lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS, state);
BUILD_TREE(lzh, types[3], lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS, state);
BUILD_TREE(lzh, types[4], lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS, state);
while (lzh.EndOfInput == 0)
{
if (lit_run != 0)
{
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
else
{
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
@@ -89,13 +90,13 @@ namespace LibMSPackSharp.Compression
len += 2;
lit_run = 0; // Not the end of a literal run
j = (int)lzh.READ_HUFFSYM_SAFE(lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
j = (int)lzh.READ_HUFFSYM_SAFE(lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
offset = j << 6;
j = (int)lzh.READ_BITS_SAFE(6, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
j = (int)lzh.READ_BITS_SAFE(6, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -115,7 +116,7 @@ namespace LibMSPackSharp.Compression
}
else
{
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
len = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -123,7 +124,7 @@ namespace LibMSPackSharp.Compression
lit_run = (len == 32) ? 0 : 1; // End of a literal run?
while (len-- > 0)
{
j = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
j = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -142,15 +143,19 @@ namespace LibMSPackSharp.Compression
return Error.MSPACK_ERR_OK;
}
private static Error BUILD_TREE(LZHKWAJStream lzh, uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
private static Error BUILD_TREE(LZHKWAJStream lzh, uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
{
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
lzh.STORE_BITS(state);
Error err = ReadLens(lzh, type, (uint)maxsymbols, lzh.MATCHLEN1_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
BufferState temp = lzh.RESTORE_BITS();
state.InputPointer = temp.InputPointer;
state.InputEnd = temp.InputEnd;
state.BitBuffer = temp.BitBuffer;
state.BitsLeft = temp.BitsLeft;
if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
return Error.MSPACK_ERR_DATAFORMAT;
@@ -162,7 +167,7 @@ namespace LibMSPackSharp.Compression
{
uint i, c, sel;
lzh.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
BufferState state = lzh.RESTORE_BITS();
switch (type)
{
@@ -177,14 +182,14 @@ namespace LibMSPackSharp.Compression
break;
case 1:
c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
c = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
sel = (uint)lzh.READ_BITS_SAFE(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
sel = (uint)lzh.READ_BITS_SAFE(1, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -194,7 +199,7 @@ namespace LibMSPackSharp.Compression
}
else
{
sel = (uint)lzh.READ_BITS_SAFE(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
sel = (uint)lzh.READ_BITS_SAFE(1, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -204,7 +209,7 @@ namespace LibMSPackSharp.Compression
}
else
{
c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
c = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -216,20 +221,20 @@ namespace LibMSPackSharp.Compression
break;
case 2:
c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
c = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
sel = (uint)lzh.READ_BITS_SAFE(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
sel = (uint)lzh.READ_BITS_SAFE(2, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
if (sel == 3)
{
c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
c = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
@@ -246,7 +251,7 @@ namespace LibMSPackSharp.Compression
case 3:
for (i = 0; i < numsyms; i++)
{
c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
c = (uint)lzh.READ_BITS_SAFE(4, state);
if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
@@ -256,7 +261,7 @@ namespace LibMSPackSharp.Compression
break;
}
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
lzh.STORE_BITS(state);
return Error.MSPACK_ERR_OK;
}

View File

@@ -8,6 +8,7 @@
*/
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
@@ -33,7 +34,7 @@ namespace LibMSPackSharp.Compression
// History window
public byte[] Window { get; set; } = new byte[LZSS.LZSS_WINDOW_SIZE];
public byte[] Window { get; set; } = new byte[LZSS_WINDOW_SIZE];
#endregion
@@ -53,10 +54,10 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Safely read bits from the buffer
/// </summary>
public long READ_BITS_SAFE(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_BITS_SAFE(int nbits, BufferState state)
{
long val = READ_BITS_MSB(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (EndOfInput != 0 && BitsLeft < EndOfInput)
long val = READ_BITS_MSB(nbits, state);
if (EndOfInput != 0 && BufferState.BitsLeft < EndOfInput)
Error = Error.MSPACK_ERR_NOMEMORY;
else
Error = Error.MSPACK_ERR_OK;
@@ -67,10 +68,10 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Safely read a symbol from a Huffman tree
/// </summary>
public long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
{
long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (EndOfInput != 0 && BitsLeft < EndOfInput)
long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols, state);
if (EndOfInput != 0 && BufferState.BitsLeft < EndOfInput)
Error = Error.MSPACK_ERR_NOMEMORY;
else
Error = Error.MSPACK_ERR_OK;
@@ -93,19 +94,19 @@ namespace LibMSPackSharp.Compression
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
/// <inheritdoc/>
public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public override void READ_BYTES(BufferState state)
{
if (i_ptr >= i_end)
if (state.InputPointer >= state.InputEnd)
{
ReadInput();
if (Error != Error.MSPACK_ERR_OK)
return;
i_ptr = InputPointer;
i_end = InputEnd;
state.InputPointer = BufferState.InputPointer;
state.InputEnd = BufferState.InputEnd;
}
INJECT_BITS_MSB(InputBuffer[i_ptr++], 8, ref bit_buffer, ref bits_left);
INJECT_BITS_MSB(InputBuffer[state.InputPointer++], 8, state);
}
/// <inheritdoc/>
@@ -129,15 +130,15 @@ namespace LibMSPackSharp.Compression
if (read == 0)
{
InputEnd = 8;
BufferState.InputEnd = 8;
InputBuffer[0] = 0;
read = 1;
}
}
// Update InputPointer and InputLength
InputPointer = 0;
InputEnd = read;
BufferState.InputPointer = 0;
BufferState.InputEnd = read;
}
}
}

View File

@@ -12,18 +12,12 @@
*/
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class LZSS
{
#region LZSS compression / decompression definitions
public const int LZSS_WINDOW_SIZE = 4096;
public const byte LZSS_WINDOW_FILL = 0x20;
#endregion
/// <summary>
/// Decompresses an LZSS stream.
///

View File

@@ -76,142 +76,12 @@
using System;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class LZX
{
#region LZX compression / decompression definitions
// Some constants defined by the LZX specification
public const int LZX_MIN_MATCH = 2;
public const int LZX_MAX_MATCH = 257;
public const int LZX_NUM_CHARS = 256;
public const int LZX_PRETREE_NUM_ELEMENTS = 20;
public const int LZX_ALIGNED_NUM_ELEMENTS = 8; // Aligned offset tree #elements
public const int LZX_NUM_PRIMARY_LENGTHS = 7; // This one missing from spec!
public const int LZX_NUM_SECONDARY_LENGTHS = 249; // Length tree #elements
// LZX huffman defines: tweak tablebits as desired
public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
public const byte LZX_PRETREE_TABLEBITS = 6;
public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8;
public const byte LZX_MAINTREE_TABLEBITS = 12;
public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1;
public const byte LZX_LENGTH_TABLEBITS = 12;
public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
public const byte LZX_ALIGNED_TABLEBITS = 7;
public const int LZX_LENTABLE_SAFETY = 64; // Table decoding overruns are allowed
public const int LZX_FRAME_SIZE = 32768; // The size of a frame in LZX
#endregion
#region LZX static data tables
/* LZX static data tables:
*
* LZX uses 'position slots' to represent match offsets. For every match,
* a small 'position slot' number and a small offset from that slot are
* encoded instead of one large offset.
*
* The number of slots is decided by how many are needed to encode the
* largest offset for a given window size. This is easy when the gap between
* slots is less than 128Kb, it's a linear relationship. But when extra_bits
* reaches its limit of 17 (because LZX can only ensure reading 17 bits of
* data at a time), we can only jump 128Kb at a time and have to start
* using more and more position slots as each window size doubles.
*
* position_base[] is an index to the position slot bases
*
* extra_bits[] states how many bits of offset-from-base data is needed.
*
* They are calculated as follows:
* extra_bits[i] = 0 where i < 4
* extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36
* extra_bits[i] = 17 where i >= 36
* position_base[0] = 0
* position_base[i] = position_base[i-1] + (1 << extra_bits[i-1])
*/
private static readonly uint[] position_slots = new uint[11]
{
30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
};
private static readonly byte[] extra_bits = new byte[36]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16
};
private static readonly uint[] position_base = new uint[290]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512,
768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768,
49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360,
786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512,
2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088,
3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664,
4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240,
6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816,
7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392,
8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968,
9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544,
10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048,
11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552,
12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056,
12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560,
13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064,
14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568,
15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072,
16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576,
17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080,
18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584,
19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088,
20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592,
21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096,
22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600,
23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104,
23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608,
24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112,
25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616,
26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120,
27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624,
28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128,
29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632,
30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136,
31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640,
32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144,
33161216, 33292288, 33423360
};
private static void ResetState(LZXDStream lzx)
{
lzx.R0 = 1;
lzx.R1 = 1;
lzx.R2 = 1;
lzx.HeaderRead = 0;
lzx.BlockRemaining = 0;
lzx.BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0;
// Initialise tables to 0 (because deltas will be applied to them)
for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++)
{
lzx.MAINTREE_len[i] = 0;
}
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
lzx.LENGTH_len[i] = 0;
}
}
#endregion
/// <summary>
/// Allocates and initialises LZX decompression state for decoding an LZX
/// stream.
@@ -268,9 +138,6 @@ namespace LibMSPackSharp.Compression
/// </returns>
public static LZXDStream Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int reset_interval, int input_buffer_size, long output_length, bool is_delta)
{
uint window_size = (uint)(1 << window_bits);
LZXDStream lzx;
if (system == null)
return null;
@@ -299,10 +166,10 @@ namespace LibMSPackSharp.Compression
return null;
// Allocate decompression state
lzx = new LZXDStream()
LZXDStream lzx = new LZXDStream()
{
// Allocate decompression window and input buffer
Window = new byte[window_size],
Window = new byte[1 << window_bits],
InputBuffer = new byte[input_buffer_size],
System = system,
@@ -321,15 +188,15 @@ namespace LibMSPackSharp.Compression
IntelFileSize = 0,
IntelStarted = false,
Error = Error.MSPACK_ERR_OK,
NumOffsets = position_slots[window_bits - 15] << 3,
NumOffsets = LZXPositionSlots[window_bits - 15] << 3,
IsDelta = is_delta,
// e8_buf
OutputPointer = 0,
OutputEnd = 0,
OutputIsE8 = true,
};
ResetState(lzx);
lzx.ResetState();
lzx.INIT_BITS();
return lzx;
@@ -351,7 +218,7 @@ namespace LibMSPackSharp.Compression
/// the length of the reference data. Cannot be longer
/// than the LZX window size.
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public static Error SetReferenceData(LZXDStream lzx, SystemImpl system, FileStream input, uint length)
{
if (lzx == null)
@@ -397,7 +264,7 @@ namespace LibMSPackSharp.Compression
return Error.MSPACK_ERR_OK;
}
// See description of outputLength in lzxd_init()
// See description of outputLength in Init()
public static void SetOutputLength(LZXDStream lzx, long outputLength)
{
if (lzx != null && outputLength > 0)
@@ -435,12 +302,8 @@ namespace LibMSPackSharp.Compression
if (lzx == null)
return Error.MSPACK_ERR_ARGS;
int match_length, length_footer, extra, verbatim_bits, bytes_todo;
int this_run, main_element, aligned_bits, j, warned = 0;
byte[] window, buf = new byte[12];
int runsrc, rundest;
uint frame_size, end_frame, match_offset, window_posn;
uint R0, R1, R2;
int warned = 0;
byte[] buf = new byte[12];
// Easy answers
if (lzx == null || (out_bytes < 0))
@@ -450,32 +313,30 @@ namespace LibMSPackSharp.Compression
return lzx.Error;
// Flush out any stored-up bytes before we begin
int i = lzx.OutputEnd - lzx.OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
int leftover_bytes = lzx.OutputEnd - lzx.OutputPointer;
if (leftover_bytes > out_bytes)
leftover_bytes = (int)out_bytes;
if (i != 0)
if (leftover_bytes != 0)
{
try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); }
try { lzx.System.Write(lzx.OutputFileHandle, lzx.OutputIsE8 ? lzx.E8Buffer : lzx.Window, lzx.OutputPointer, leftover_bytes); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
lzx.OutputPointer += i;
lzx.Offset += i;
out_bytes -= i;
lzx.OutputPointer += leftover_bytes;
lzx.Offset += leftover_bytes;
out_bytes -= leftover_bytes;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
// Restore local state
lzx.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
window = lzx.Window;
window_posn = lzx.WindowPosition;
R0 = lzx.R0;
R1 = lzx.R1;
R2 = lzx.R2;
BufferState state = lzx.RESTORE_BITS();
byte[] window = lzx.Window;
int window_posn = lzx.WindowPosition;
uint[] R = lzx.R;
end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
uint end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
while (lzx.Frame < end_frame)
{
@@ -494,410 +355,93 @@ namespace LibMSPackSharp.Compression
}
// Re-read the intel header and reset the huffman lengths
ResetState(lzx);
R0 = lzx.R0;
R1 = lzx.R1;
R2 = lzx.R2;
lzx.ResetState();
R = lzx.R;
}
// LZX DELTA format has chunk_size, not present in LZX format
if (lzx.IsDelta)
{
lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lzx.REMOVE_BITS_MSB(16, ref bit_buffer, ref bits_left);
lzx.ENSURE_BITS(16, state);
state.REMOVE_BITS_MSB(16);
}
//// Read header if necessary
//if (lzx.HeaderRead == 0)
//{
// // Read 1 bit. If bit=0, intel_filesize = 0.
// // If bit=1, read intel filesize (32 bits)
// int j = 0;
// int i = (int)lzx.READ_BITS_MSB(1, state);
// if (i != 0)
// {
// i = (int)lzx.READ_BITS_MSB(16, state);
// j = (int)lzx.READ_BITS_MSB(16, state);
// }
// lzx.IntelFileSize = (i << 16) | j;
// lzx.HeaderRead = 1;
//}
// Calculate size of frame: all frames are 32k except the final frame
// which is 32kb or less. this can only be calculated when lzx.Length
// has been filled in.
frame_size = LZX_FRAME_SIZE;
uint frame_size = LZX_FRAME_SIZE;
if (lzx.Length != 0 && (lzx.Length - lzx.Offset) < frame_size)
frame_size = (uint)(lzx.Length - lzx.Offset);
// Decode until one more frame is available
bytes_todo = (int)(lzx.FramePosition + frame_size - window_posn);
int bytes_todo = (int)(lzx.FramePosition + frame_size - window_posn);
while (bytes_todo > 0)
{
// Initialise new block, if one is needed
if (lzx.BlockRemaining == 0)
// Realign if previous block was an odd-sized UNCOMPRESSED block
if ((lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED) && (lzx.BlockLength & 1) != 0)
{
// Realign if previous block was an odd-sized UNCOMPRESSED block
if ((lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED) && (lzx.BlockLength & 1) != 0)
{
lzx.READ_IF_NEEDED(ref i_ptr, ref i_end);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
lzx.READ_IF_NEEDED(state);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr++;
}
// Read block type (3 bits) and block length (24 bits)
// THIS IS NOT 3 BECAUSE OF OTHER CODE I FOUND
lzx.BlockType = (LZXBlockType)lzx.READ_BITS_MSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Read header if necessary
if (lzx.HeaderRead == 0)
{
// Read 1 bit. if bit=0, intel_filesize = 0.
// if bit=1, read intel filesize (32 bits)
j = 0;
i = (int)lzx.READ_BITS_MSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (i != 0)
{
i = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
j = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
}
lzx.IntelFileSize = (i << 16) | j;
lzx.HeaderRead = 1;
}
i = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
j = (int)lzx.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lzx.BlockRemaining = lzx.BlockLength = (uint)((i << 8) | j);
// Console.WriteLine($"New block t {lzx.BlockType} len {lzx.BlockLength}");
// Read individual block headers
switch (lzx.BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
// Read lengths of and build aligned huffman decoding tree
for (i = 0; i < 8; i++)
{
j = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lzx.ALIGNED_len[i] = (byte)j;
}
BUILD_TABLE(lzx, lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(lzx, lzx.MAINTREE_len, 0, 256, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
READ_LENGTHS(lzx, lzx.MAINTREE_len, 256, LZX_NUM_CHARS + lzx.NumOffsets, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
BUILD_TABLE(lzx, lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
// If the literal 0xE8 is anywhere in the block...
if (lzx.MAINTREE_len[0xE8] != 0)
lzx.IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(lzx, lzx.LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
BUILD_TABLE_MAYBE_EMPTY(lzx);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
break;
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(lzx, lzx.MAINTREE_len, 0, 256, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
READ_LENGTHS(lzx, lzx.MAINTREE_len, 256, LZX_NUM_CHARS + lzx.NumOffsets, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
BUILD_TABLE(lzx, lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
// If the literal 0xE8 is anywhere in the block...
if (lzx.MAINTREE_len[0xE8] != 0)
lzx.IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(lzx, lzx.LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
BUILD_TABLE_MAYBE_EMPTY(lzx);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// Because we can't assume otherwise
lzx.IntelStarted = true;
// Read 1-16 (not 0-15) bits to align to bytes
if (bits_left == 0)
lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
bits_left = 0; bit_buffer = 0;
// Read 12 bytes of stored R0 / R1 / R2 values
for (rundest = 0, i = 0; i < 12; i++)
{
lzx.READ_IF_NEEDED(ref i_ptr, ref i_end);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
buf[rundest++] = lzx.InputBuffer[i_ptr++];
}
R0 = (uint)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
R1 = (uint)(buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24));
R2 = (uint)(buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24));
break;
default:
Console.WriteLine("Bad block type");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
state.InputPointer++;
}
lzx.ReadBlockHeader(buf, ref R, state);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
// Decode more of the block:
// run = min(what's available, what's needed)
this_run = (int)lzx.BlockRemaining;
if (this_run > bytes_todo)
this_run = bytes_todo;
int this_run = Math.Min(lzx.BlockRemaining, bytes_todo);
// Assume we decode exactly this_run bytes, for now
bytes_todo -= this_run;
lzx.BlockRemaining -= (uint)this_run;
lzx.BlockRemaining -= this_run;
// Decode at least this_run bytes
switch (lzx.BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
while (this_run > 0)
{
main_element = (int)lzx.READ_HUFFSYM_MSB(lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (main_element < LZX_NUM_CHARS)
{
// Literal: 0 to LZX_NUM_CHARS-1
window[window_posn++] = (byte)main_element;
this_run--;
}
else
{
// Match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits))
main_element -= LZX_NUM_CHARS;
lzx.DecompressBlock(window, ref window_posn, ref this_run, ref R, state);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
// Get match length
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
{
if (lzx.LENGTH_empty != 0)
{
Console.WriteLine("LENGTH symbol needed but tree is empty");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
length_footer = (int)lzx.READ_HUFFSYM_MSB(lzx.LENGTH_table, lzx.LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
// Get match offset
switch ((match_offset = (uint)(main_element >> 3)))
{
case 0:
match_offset = R0;
break;
case 1:
match_offset = R1;
R1 = R0;
R0 = match_offset;
break;
case 2:
match_offset = R2;
R2 = R0;
R0 = match_offset;
break;
default:
if (lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM)
{
if (match_offset == 3)
{
match_offset = 1;
}
else
{
extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = (uint)(position_base[match_offset] - 2 + verbatim_bits);
}
}
// LZX_BLOCKTYPE_ALIGNED
else
{
extra = (match_offset >= 36) ? 17 : extra_bits[match_offset];
match_offset = position_base[match_offset] - 2;
// >3: verbatim and aligned bits
if (extra > 3)
{
extra -= 3;
verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset += (uint)(verbatim_bits << 3);
aligned_bits = (int)lzx.READ_HUFFSYM_MSB(lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset += (uint)aligned_bits;
}
// 3: aligned bits only
else if (extra == 3)
{
aligned_bits = (int)lzx.READ_HUFFSYM_MSB(lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset += (uint)aligned_bits;
}
// 1-2: verbatim bits only
else if (extra > 0)
{
verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset += (uint)verbatim_bits;
}
// 0: not defined in LZX specification!
else
{
match_offset = 1;
}
}
// Update repeated offset LRU queue
R2 = R1; R1 = R0; R0 = match_offset;
break;
}
// LZX DELTA uses max match length to signal even longer match
if (match_length == LZX_MAX_MATCH && lzx.IsDelta)
{
int extra_len;
// 4 entry huffman tree
lzx.ENSURE_BITS(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// '0' . 8 extra length bits
if (lzx.PEEK_BITS_MSB(1, bit_buffer) == 0)
{
lzx.REMOVE_BITS_MSB(1, ref bit_buffer, ref bits_left);
extra_len = (int)lzx.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
}
// '10' . 10 extra length bits + 0x100
else if (lzx.PEEK_BITS_MSB(2, bit_buffer) == 2)
{
lzx.REMOVE_BITS_MSB(2, ref bit_buffer, ref bits_left);
extra_len = (int)lzx.READ_BITS_MSB(10, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra_len += 0x100;
}
// '110' . 12 extra length bits + 0x500
else if (lzx.PEEK_BITS_MSB(3, bit_buffer) == 6)
{
lzx.REMOVE_BITS_MSB(3, ref bit_buffer, ref bits_left);
extra_len = (int)lzx.READ_BITS_MSB(12, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra_len += 0x500;
}
// '111' . 15 extra length bits
else
{
lzx.REMOVE_BITS_MSB(3, ref bit_buffer, ref bits_left);
extra_len = (int)lzx.READ_BITS_MSB(15, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
}
match_length += extra_len;
}
if ((window_posn + match_length) > lzx.WindowSize)
{
Console.WriteLine("Match ran over window wrap");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
// Copy match
rundest = (int)window_posn;
i = match_length;
// Does match offset wrap the window?
if (match_offset > window_posn)
{
if (match_offset > lzx.Offset && (match_offset - window_posn) > lzx.ReferenceDataSize)
{
Console.WriteLine("Match offset beyond LZX stream");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
// j = length from match offset to end of window
j = (int)(match_offset - window_posn);
if (j > (int)lzx.WindowSize)
{
Console.WriteLine("Match offset beyond window boundaries");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
runsrc = (int)(lzx.WindowSize - j);
if (j < i)
{
// If match goes over the window edge, do two copy runs
i -= j;
while (j-- > 0)
{
window[rundest++] = window[runsrc++];
}
runsrc = 0;
}
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
else
{
runsrc = (int)(rundest - match_offset);
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
this_run -= match_length;
window_posn += (uint)match_length;
}
}
// If the literal 0xE8 is anywhere in the block...
if (lzx.MAINTREE_len[0xE8] != 0)
lzx.IntelStarted = true;
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// As this_run is limited not to wrap a frame, this also means it
// won't wrap the window (as the window is a multiple of 32k)
rundest = (int)window_posn;
window_posn += (uint)this_run;
int rundest = window_posn;
window_posn += this_run;
while (this_run > 0)
{
if ((i = i_end - i_ptr) == 0)
int i = state.InputEnd - state.InputPointer;
if (i == 0)
{
lzx.READ_IF_NEEDED(ref i_ptr, ref i_end);
lzx.READ_IF_NEEDED(state);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
}
@@ -906,13 +450,17 @@ namespace LibMSPackSharp.Compression
if (i > this_run)
i = this_run;
Array.Copy(lzx.InputBuffer, i_ptr, window, rundest, i);
Array.Copy(lzx.InputBuffer, state.InputPointer, window, rundest, i);
rundest += i;
i_ptr += i;
state.InputPointer += i;
this_run -= i;
}
}
// Because we can't assume otherwise
lzx.IntelStarted = true;
break;
default:
@@ -928,7 +476,7 @@ namespace LibMSPackSharp.Compression
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
lzx.BlockRemaining -= (uint)-this_run;
lzx.BlockRemaining -= -this_run;
}
}
@@ -940,10 +488,10 @@ namespace LibMSPackSharp.Compression
}
// Re-align input bitstream
if (bits_left > 0)
lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if ((bits_left & 15) != 0)
lzx.REMOVE_BITS_MSB(bits_left & 15, ref bit_buffer, ref bits_left);
if (state.BitsLeft > 0)
lzx.ENSURE_BITS(16, state);
if ((state.BitsLeft & 15) != 0)
state.REMOVE_BITS_MSB(state.BitsLeft & 15);
// Check that we've used all of the previous frame first
if (lzx.OutputPointer != lzx.OutputEnd)
@@ -955,59 +503,24 @@ namespace LibMSPackSharp.Compression
// Does this intel block _really_ need decoding?
if (lzx.IntelStarted && lzx.IntelFileSize != 0 && (lzx.Frame < 32768) && (frame_size > 10))
{
int data = 0;
int dataend = (int)(frame_size - 10);
int curpos = (int)lzx.Offset;
int filesize = lzx.IntelFileSize;
int abs_off, rel_off;
// Copy e8 block to the e8 buffer and tweak if needed
lzx.OutputPointer = data;
Array.Copy(lzx.Window, lzx.FramePosition, lzx.e8_buf, data, frame_size);
while (data < dataend)
{
if (lzx.e8_buf[data++] != 0xE8)
{
curpos++;
continue;
}
abs_off = lzx.e8_buf[data + 0] | (lzx.e8_buf[data + 1] << 8) | (lzx.e8_buf[data + 2] << 16) | (lzx.e8_buf[data + 3] << 24);
if ((abs_off >= -curpos) && (abs_off < filesize))
{
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
lzx.e8_buf[data + 0] = (byte)rel_off;
lzx.e8_buf[data + 1] = (byte)(rel_off >> 8);
lzx.e8_buf[data + 2] = (byte)(rel_off >> 16);
lzx.e8_buf[data + 3] = (byte)(rel_off >> 24);
}
data += 4;
curpos += 5;
}
lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size);
// Write a frame
i = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
lzx.UndoE8Preprocessing(frame_size);
}
else
{
lzx.OutputIsE8 = false;
lzx.OutputPointer = (int)lzx.FramePosition;
lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size);
// Write a frame
i = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { lzx.System.Write(lzx.OutputFileHandle, lzx.Window, lzx.OutputPointer, i); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
}
lzx.OutputPointer += i;
lzx.Offset += i;
out_bytes -= i;
lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size);
// Write a frame
int new_out_bytes = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { lzx.System.Write(lzx.OutputFileHandle, lzx.OutputIsE8 ? lzx.E8Buffer : lzx.Window, lzx.OutputPointer, new_out_bytes); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
lzx.OutputPointer += new_out_bytes;
lzx.Offset += new_out_bytes;
out_bytes -= new_out_bytes;
// Advance frame start position
lzx.FramePosition += frame_size;
@@ -1028,131 +541,9 @@ namespace LibMSPackSharp.Compression
}
// Store local state
lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
lzx.STORE_BITS(state);
lzx.WindowPosition = window_posn;
lzx.R0 = R0;
lzx.R1 = R1;
lzx.R2 = R2;
return Error.MSPACK_ERR_OK;
}
private static Error BUILD_TABLE(LZXDStream lzx, ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
{
Console.WriteLine($"Failed to build table");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
return lzx.Error = Error.MSPACK_ERR_OK;
}
private static Error BUILD_TABLE_MAYBE_EMPTY(LZXDStream lzx)
{
lzx.LENGTH_empty = 0;
if (!CompressionStream.MakeDecodeTableMSB(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, lzx.LENGTH_len, lzx.LENGTH_table))
{
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
if (lzx.LENGTH_len[i] > 0)
{
Console.WriteLine("Failed to build table");
return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
}
}
// Empty tree - allow it, but don't decode symbols with it
lzx.LENGTH_empty = 1;
}
return lzx.Error = Error.MSPACK_ERR_OK;
}
private static Error READ_LENGTHS(LZXDStream lzx, byte[] lengths, uint first, uint last, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
{
lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
if (ReadLens(lzx, lengths, first, last) != Error.MSPACK_ERR_OK)
return lzx.Error;
lzx.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
return lzx.Error = Error.MSPACK_ERR_OK;
}
private static Error ReadLens(LZXDStream lzx, byte[] lens, uint first, uint last)
{
uint x, y;
int z;
lzx.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
// Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits)
for (x = 0; x < 20; x++)
{
y = (uint)lzx.READ_BITS_MSB(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lzx.PRETREE_len[x] = (byte)y;
}
BUILD_TABLE(lzx, lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
if (lzx.Error != Error.MSPACK_ERR_OK)
return lzx.Error;
for (x = first; x < last;)
{
z = (int)lzx.READ_HUFFSYM_MSB(lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Code = 17, run of ([read 4 bits]+4) zeros
if (z == 17)
{
y = (uint)lzx.READ_BITS_MSB(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
y += 4;
while (y-- != 0)
{
lens[x++] = 0;
}
}
// Code = 18, run of ([read 5 bits]+20) zeros
else if (z == 18)
{
y = (uint)lzx.READ_BITS_MSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
y += 20;
while (y-- != 0)
{
lens[x++] = 0;
}
}
// Code = 19, run of ([read 1 bit]+4) [read huffman symbol]
else if (z == 19)
{
y = (uint)lzx.READ_BITS_MSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
y += 4;
z = (int)lzx.READ_HUFFSYM_MSB(lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
z = lens[x] - z;
if (z < 0)
z += 17;
while (y-- != 0)
{
lens[x++] = (byte)z;
}
}
// Code = 0 to 16, delta current length entry
else
{
z = lens[x] - z;
if (z < 0)
z += 17;
lens[x++] = (byte)z;
}
}
lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
lzx.R = R;
return Error.MSPACK_ERR_OK;
}

View File

@@ -10,6 +10,9 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class LZXDStream : CompressionStream
@@ -49,7 +52,7 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// Decompression offset within window
/// </summary>
public uint WindowPosition { get; set; }
public int WindowPosition { get; set; }
/// <summary>
/// Current frame offset within in window
@@ -69,27 +72,17 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R0 { get; set; }
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R1 { get; set; }
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R2 { get; set; }
public uint[] R { get; set; } = new uint[3];
/// <summary>
/// Uncompressed length of this LZX block
/// </summary>
public uint BlockLength { get; set; }
public int BlockLength { get; set; }
/// <summary>
/// Uncompressed bytes still left to decode
/// </summary>
public uint BlockRemaining { get; set; }
public int BlockRemaining { get; set; }
/// <summary>
/// Magic header value used for transform
@@ -118,26 +111,564 @@ namespace LibMSPackSharp.Compression
#region Huffman code lengths
public byte[] PRETREE_len { get; set; } = new byte[LZX.LZX_PRETREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] MAINTREE_len { get; set; } = new byte[LZX.LZX_MAINTREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] LENGTH_len { get; set; } = new byte[LZX.LZX_LENGTH_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] ALIGNED_len { get; set; } = new byte[LZX.LZX_ALIGNED_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] PRETREE_len { get; set; } = new byte[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] MAINTREE_len { get; set; } = new byte[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] LENGTH_len { get; set; } = new byte[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] ALIGNED_len { get; set; } = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
#endregion
#region Huffman decoding tables
public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX.LZX_PRETREE_TABLEBITS) + (LZX.LZX_PRETREE_MAXSYMBOLS * 2)];
public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX.LZX_MAINTREE_TABLEBITS) + (LZX.LZX_MAINTREE_MAXSYMBOLS * 2)];
public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX.LZX_LENGTH_TABLEBITS) + (LZX.LZX_LENGTH_MAXSYMBOLS * 2)];
public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX.LZX_ALIGNED_TABLEBITS) + (LZX.LZX_ALIGNED_MAXSYMBOLS * 2)];
public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS * 2)];
public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS * 2)];
public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS * 2)];
public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS * 2)];
#endregion
public byte LENGTH_empty { get; set; }
// This is used purely for doing the intel E8 transform
public byte[] e8_buf { get; set; } = new byte[LZX.LZX_FRAME_SIZE];
public byte[] E8Buffer { get; set; } = new byte[LZX_FRAME_SIZE];
/// <summary>
/// Is the output pointer referring to E8?
/// </summary>
public bool OutputIsE8 { get; set; }
#endregion
#region Specialty Methods
public Error DecompressBlock(byte[] window, ref int window_posn, ref int this_run, ref uint[] R, BufferState state)
{
while (this_run > 0)
{
int main_element = (int)READ_HUFFSYM_MSB(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, state);
if (main_element < LZX_NUM_CHARS)
{
// Literal: 0 to LZX_NUM_CHARS-1
window[window_posn++] = (byte)main_element;
this_run--;
}
else
{
// Match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits))
main_element -= LZX_NUM_CHARS;
// Get match length
int match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
{
if (LENGTH_empty != 0)
{
Console.WriteLine("LENGTH symbol needed but tree is empty");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
int length_footer = (int)READ_HUFFSYM_MSB(LENGTH_table, LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, state);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
// Get match offset
uint match_offset = (uint)(main_element >> 3);
switch (match_offset)
{
case 0:
match_offset = R[0];
break;
case 1:
match_offset = R[1];
R[1] = R[0];
R[0] = match_offset;
break;
case 2:
match_offset = R[2];
R[2] = R[0];
R[0] = match_offset;
break;
default:
if (BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM)
{
if (match_offset == 3)
{
match_offset = 1;
}
else
{
int extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset];
int verbatim_bits = (int)READ_BITS_MSB(extra, state);
match_offset = (uint)(LZXPositionBase[match_offset] - 2 + verbatim_bits);
}
}
// LZX_BLOCKTYPE_ALIGNED
else
{
int extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset];
match_offset = LZXPositionBase[match_offset] - 2;
// >3: verbatim and aligned bits
if (extra > 3)
{
extra -= 3;
int verbatim_bits = (int)READ_BITS_MSB(extra, state);
match_offset += (uint)(verbatim_bits << 3);
int aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, state);
match_offset += (uint)aligned_bits;
}
// 3: aligned bits only
else if (extra == 3)
{
int aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, state);
match_offset += (uint)aligned_bits;
}
// 1-2: verbatim bits only
else if (extra > 0)
{
int verbatim_bits = (int)READ_BITS_MSB(extra, state);
match_offset += (uint)verbatim_bits;
}
// 0: not defined in LZX specification!
else
{
match_offset = 1;
}
}
// Update repeated offset LRU queue
R[2] = R[1]; R[1] = R[0]; R[0] = match_offset;
break;
}
// LZX DELTA uses max match length to signal even longer match
if (match_length == LZX_MAX_MATCH && IsDelta)
{
int extra_len;
// 4 entry huffman tree
ENSURE_BITS(3, state);
// '0' . 8 extra length bits
if (PEEK_BITS_MSB(1, state.BitBuffer) == 0)
{
state.REMOVE_BITS_MSB(1);
extra_len = (int)READ_BITS_MSB(8, state);
}
// '10' . 10 extra length bits + 0x100
else if (PEEK_BITS_MSB(2, state.BitBuffer) == 2)
{
state.REMOVE_BITS_MSB(2);
extra_len = (int)READ_BITS_MSB(10, state);
extra_len += 0x100;
}
// '110' . 12 extra length bits + 0x500
else if (PEEK_BITS_MSB(3, state.BitBuffer) == 6)
{
state.REMOVE_BITS_MSB(3);
extra_len = (int)READ_BITS_MSB(12, state);
extra_len += 0x500;
}
// '111' . 15 extra length bits
else
{
state.REMOVE_BITS_MSB(3);
extra_len = (int)READ_BITS_MSB(15, state);
}
match_length += extra_len;
}
if ((window_posn + match_length) > WindowSize)
{
Console.WriteLine("Match ran over window wrap");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Copy match
int rundest = window_posn;
int i = match_length;
// Does match offset wrap the window?
if (match_offset > window_posn)
{
if (match_offset > Offset && (match_offset - window_posn) > ReferenceDataSize)
{
Console.WriteLine("Match offset beyond LZX stream");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// j = length from match offset to end of window
int j = (int)(match_offset - window_posn);
if (j > (int)WindowSize)
{
Console.WriteLine("Match offset beyond window boundaries");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
int runsrc = (int)(WindowSize - j);
if (j < i)
{
// If match goes over the window edge, do two copy runs
i -= j;
while (j-- > 0)
{
window[rundest++] = window[runsrc++];
}
runsrc = 0;
}
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
else
{
int runsrc = (int)(rundest - match_offset);
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
this_run -= match_length;
window_posn += match_length;
}
}
return Error = Error.MSPACK_ERR_OK;
}
public Error ReadBlockHeader(byte[] buffer, ref uint[] R, BufferState state)
{
ENSURE_BITS(4, state);
// Read block type (3 bits) and block length (24 bits)
byte block_type = (byte)READ_BITS_MSB(3, state);
BlockType = (LZXBlockType)block_type;
// Read the block size
int block_size;
if (READ_BITS_MSB(1, state) == 1)
{
block_size = LZX_FRAME_SIZE;
}
else
{
int tmp;
block_size = 0;
tmp = (int)READ_BITS_MSB(8, state);
block_size |= tmp;
tmp = (int)READ_BITS_MSB(8, state);
block_size <<= 8;
block_size |= tmp;
if (WindowSize >= 65536)
{
tmp = (int)READ_BITS_MSB(8, state);
block_size <<= 8;
block_size |= tmp;
}
}
BlockRemaining = BlockLength = block_size;
Console.WriteLine($"New block t {BlockType} len {BlockLength}");
// Read individual block headers
switch (BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
// Read lengths of and build aligned huffman decoding tree
for (byte i = 0; i < 8; i++)
{
ALIGNED_len[i] = (byte)READ_BITS_MSB(3, state);
}
BUILD_TABLE(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(MAINTREE_len, 0, 256, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
BUILD_TABLE_MAYBE_EMPTY();
if (Error != Error.MSPACK_ERR_OK)
return Error;
break;
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
// Read lengths of and build main huffman decoding tree
READ_LENGTHS(MAINTREE_len, 0, 256, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
// If the literal 0xE8 is anywhere in the block...
if (MAINTREE_len[0xE8] != 0)
IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
BUILD_TABLE_MAYBE_EMPTY();
if (Error != Error.MSPACK_ERR_OK)
return Error;
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// Read 1-16 (not 0-15) bits to align to bytes
if (state.BitsLeft == 0)
ENSURE_BITS(16, state);
state.BitsLeft = 0; state.BitBuffer = 0;
// Read 12 bytes of stored R[0] / R[1] / R[2] values
for (int rundest = 0, k = 0; k < 12; k++)
{
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return Error;
buffer[rundest++] = InputBuffer[state.InputPointer++];
}
// TODO: uint[] R should be a part of a state object
R[0] = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24));
R[1] = (uint)(buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24));
R[2] = (uint)(buffer[8] | (buffer[9] << 8) | (buffer[10] << 16) | (buffer[11] << 24));
break;
default:
Console.WriteLine($"Bad block type: {BlockType}");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error = Error.MSPACK_ERR_OK;
}
public Error ReadLens(byte[] lens, uint first, uint last)
{
BufferState state = RESTORE_BITS();
// Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits)
for (int i = 0; i < LZX_PRETREE_MAXSYMBOLS; i++)
{
uint y = (uint)READ_BITS_MSB(4, state);
PRETREE_len[i] = (byte)y;
}
BUILD_TABLE(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
for (uint lensPtr = first; lensPtr < last;)
{
uint num_zeroes, num_same;
int tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, state);
switch (tree_code)
{
// Code = 17, run of ([read 4 bits]+4) zeros
case 17:
num_zeroes = (uint)READ_BITS_MSB(4, state);
num_zeroes += 4;
while (num_zeroes-- != 0)
{
lens[lensPtr++] = 0;
}
break;
// Code = 18, run of ([read 5 bits]+20) zeros
case 18:
num_zeroes = (uint)READ_BITS_MSB(5, state);
num_zeroes += 20;
while (num_zeroes-- != 0)
{
lens[lensPtr++] = 0;
}
break;
// Code = 19, run of ([read 1 bit]+4) [read huffman symbol]
case 19:
num_same = (uint)READ_BITS_MSB(1, state);
num_same += 4;
tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, state);
tree_code = lens[lensPtr] - tree_code;
if (tree_code < 0)
tree_code += 17;
while (num_same-- != 0)
{
lens[lensPtr++] = (byte)tree_code;
}
break;
// Code = 0 to 16, delta current length entry
default:
tree_code = lens[lensPtr] - tree_code;
if (tree_code < 0)
tree_code += 17;
lens[lensPtr++] = (byte)tree_code;
break;
}
}
STORE_BITS(state);
return Error.MSPACK_ERR_OK;
}
public void ResetState()
{
R[0] = 1;
R[1] = 1;
R[2] = 1;
HeaderRead = 0;
BlockRemaining = 0;
BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0;
// Initialise tables to 0 (because deltas will be applied to them)
for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++)
{
MAINTREE_len[i] = 0;
}
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
LENGTH_len[i] = 0;
}
}
public void UndoE8Preprocessing(uint frame_size)
{
int data = 0;
int dataend = (int)(frame_size - 10);
int curpos = (int)Offset;
int filesize = IntelFileSize;
int abs_off, rel_off;
// Copy e8 block to the e8 buffer and tweak if needed
OutputIsE8 = true;
OutputPointer = data;
Array.Copy(Window, FramePosition, E8Buffer, data, frame_size);
while (data < dataend)
{
if (E8Buffer[data++] != 0xE8)
{
curpos++;
continue;
}
abs_off = E8Buffer[data + 0] | (E8Buffer[data + 1] << 8) | (E8Buffer[data + 2] << 16) | (E8Buffer[data + 3] << 24);
if ((abs_off >= -curpos) && (abs_off < filesize))
{
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
E8Buffer[data + 0] = (byte)rel_off;
E8Buffer[data + 1] = (byte)(rel_off >> 8);
E8Buffer[data + 2] = (byte)(rel_off >> 16);
E8Buffer[data + 3] = (byte)(rel_off >> 24);
}
data += 4;
curpos += 5;
}
}
private Error BUILD_TABLE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
if (!MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
{
Console.WriteLine($"Failed to build table");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error = Error.MSPACK_ERR_OK;
}
private Error BUILD_TABLE_MAYBE_EMPTY()
{
LENGTH_empty = 0;
if (!MakeDecodeTableMSB(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, LENGTH_len, LENGTH_table))
{
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
if (LENGTH_len[i] > 0)
{
Console.WriteLine("Failed to build table");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
}
// Empty tree - allow it, but don't decode symbols with it
LENGTH_empty = 1;
}
return Error = Error.MSPACK_ERR_OK;
}
private Error READ_LENGTHS(byte[] lengths, uint first, uint last, BufferState state)
{
STORE_BITS(state);
if (ReadLens(lengths, first, last) != Error.MSPACK_ERR_OK)
return Error;
BufferState temp = RESTORE_BITS();
state.InputPointer = temp.InputPointer;
state.InputEnd = temp.InputEnd;
state.BitBuffer = temp.BitBuffer;
state.BitsLeft = temp.BitsLeft;
return Error = Error.MSPACK_ERR_OK;
}
#endregion
@@ -145,20 +676,20 @@ namespace LibMSPackSharp.Compression
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
/// <inheritdoc/>
public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public override void READ_BYTES(BufferState state)
{
READ_IF_NEEDED(ref i_ptr, ref i_end);
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return;
byte b0 = InputBuffer[i_ptr++];
byte b0 = InputBuffer[state.InputPointer++];
READ_IF_NEEDED(ref i_ptr, ref i_end);
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return;
byte b1 = InputBuffer[i_ptr++];
INJECT_BITS_MSB((b1 << 8) | b0, 16, ref bit_buffer, ref bits_left);
byte b1 = InputBuffer[state.InputPointer++];
INJECT_BITS_MSB((b1 << 8) | b0, 16, state);
}
}
}

View File

@@ -12,75 +12,12 @@
using System;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class MSZIP
{
#region MSZIP (deflate) compression / (inflate) decompression definitions
public const int MSZIP_FRAME_SIZE = 32768; // Size of LZ history window
public const int MSZIP_LITERAL_MAXSYMBOLS = 288; // literal/length huffman tree
public const int MSZIP_LITERAL_TABLEBITS = 9;
public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; // Distance huffman tree
public const int MSZIP_DISTANCE_TABLEBITS = 6;
// If there are less direct lookup entries than symbols, the longer
// code pointers will be <= maxsymbols. This must not happen, or we
// will decode entries badly
//public const int MSZIP_LITERAL_TABLESIZE = (MSZIP_LITERAL_MAXSYMBOLS * 4);
public const int MSZIP_LITERAL_TABLESIZE = ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2));
//public const int MSZIP_DISTANCE_TABLESIZE = (MSZIP_DISTANCE_MAXSYMBOLS * 4);
public const int MSZIP_DISTANCE_TABLESIZE = ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2));
/// <summary>
/// Match lengths for literal codes 257.. 285
/// </summary>
private static readonly ushort[] lit_lengths = new ushort[29]
{
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
/// <summary>
/// Match offsets for distance codes 0 .. 29
/// </summary>
private static readonly ushort[] dist_offsets = new ushort[30]
{
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
/// <summary>
/// Extra bits required for literal codes 257.. 285
/// </summary>
private static readonly byte[] lit_extrabits = new byte[29]
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
/// <summary>
/// Extra bits required for distance codes 0 .. 29
/// </summary>
private static readonly byte[] dist_extrabits = new byte[30]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
/// <summary>
/// The order of the bit length Huffman code lengths
/// </summary>
private static readonly byte[] bitlen_order = new byte[19]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
#endregion
/// <summary>
/// Allocates MS-ZIP decompression stream for decoding the given stream.
///
@@ -120,12 +57,16 @@ namespace LibMSPackSharp.Compression
RepairMode = repair_mode,
FlushWindow = FlushWindow,
InputPointer = 0,
InputEnd = 0,
OutputPointer = 0,
OutputEnd = 0,
BitBuffer = 0,
BitsLeft = 0,
BufferState = new BufferState()
{
InputPointer = 0,
InputEnd = 0,
BitBuffer = 0,
BitsLeft = 0,
}
};
}
@@ -159,7 +100,7 @@ namespace LibMSPackSharp.Compression
int bits_left;
int i_ptr, i_end;
int i, state;
int i, readState;
Error error;
// Easy answers
@@ -188,32 +129,32 @@ namespace LibMSPackSharp.Compression
while (out_bytes > 0)
{
// Unpack another block
zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
BufferState state = zip.RESTORE_BITS();
// Skip to next read 'CK' header
i = bits_left & 7;
i = state.BitsLeft & 7;
// Align to bytestream
zip.REMOVE_BITS_LSB(i, ref bit_buffer, ref bits_left);
state.REMOVE_BITS_LSB(i);
state = 0;
readState = 0;
do
{
i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
i = (int)zip.READ_BITS_LSB(8, state);
if (i == 'C')
state = 1;
else if ((state == 1) && (i == 'K'))
state = 2;
readState = 1;
else if ((readState == 1) && (i == 'K'))
readState = 2;
else
state = 0;
} while (state != 2);
readState = 0;
} while (readState != 2);
// Inflate a block, repair and realign if necessary
zip.WindowPosition = 0;
zip.BytesOutput = 0;
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
zip.STORE_BITS(state);
if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK)
{
@@ -269,36 +210,31 @@ namespace LibMSPackSharp.Compression
/// </summary>
public static Error DecompressKWAJ(MSZIPDStream zip)
{
// For the bit buffer
uint bit_buffer;
int bits_left;
int i_ptr, i_end;
int i, block_len;
Error error;
// Unpack blocks until block_len == 0
for (; ; )
{
zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
BufferState state = zip.RESTORE_BITS();
// Align to bytestream, read block_len
i = bits_left & 7;
zip.REMOVE_BITS_LSB(i, ref bit_buffer, ref bits_left);
i = state.BitsLeft & 7;
state.REMOVE_BITS_LSB(i);
block_len = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
block_len = (int)zip.READ_BITS_LSB(8, state);
i = (int)zip.READ_BITS_LSB(8, state);
block_len |= i << 8;
if (block_len == 0)
break;
// Read "CK" header
i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
i = (int)zip.READ_BITS_LSB(8, state);
if (i != 'C')
return Error.MSPACK_ERR_DATAFORMAT;
i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
i = (int)zip.READ_BITS_LSB(8, state);
if (i != 'K')
return Error.MSPACK_ERR_DATAFORMAT;
@@ -306,7 +242,7 @@ namespace LibMSPackSharp.Compression
zip.WindowPosition = 0;
zip.BytesOutput = 0;
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
zip.STORE_BITS(state);
if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK)
{
@@ -331,16 +267,16 @@ namespace LibMSPackSharp.Compression
byte[] lens = new byte[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS];
uint lit_codes, dist_codes, code, last_code = 0, bitlen_codes, i, run;
zip.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
BufferState state = zip.RESTORE_BITS();
// Read the number of codes
lit_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lit_codes = (uint)zip.READ_BITS_LSB(5, state);
lit_codes += 257;
dist_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
dist_codes = (uint)zip.READ_BITS_LSB(5, state);
dist_codes += 1;
bitlen_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
bitlen_codes = (uint)zip.READ_BITS_LSB(5, state);
bitlen_codes += 4;
if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS)
@@ -351,12 +287,12 @@ namespace LibMSPackSharp.Compression
// Read in the bit lengths in their unusual order
for (i = 0; i < bitlen_codes; i++)
{
bl_len[bitlen_order[i]] = (byte)zip.READ_BITS_LSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
bl_len[BitLengthOrder[i]] = (byte)zip.READ_BITS_LSB(3, state);
}
while (i < 19)
{
bl_len[bitlen_order[i++]] = 0;
bl_len[BitLengthOrder[i++]] = 0;
}
// Create decoding table with an immediate lookup
@@ -368,9 +304,9 @@ namespace LibMSPackSharp.Compression
{
// Single-level huffman lookup
zip.ENSURE_BITS(7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
code = bl_table[zip.PEEK_BITS_LSB(7, bit_buffer)];
zip.REMOVE_BITS_LSB(bl_len[code], ref bit_buffer, ref bits_left);
zip.ENSURE_BITS(7, state);
code = bl_table[zip.PEEK_BITS_LSB(7, state.BitBuffer)];
state.REMOVE_BITS_LSB(bl_len[code]);
if (code < 16)
{
@@ -381,19 +317,19 @@ namespace LibMSPackSharp.Compression
switch (code)
{
case 16:
run = (uint)zip.READ_BITS_LSB(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run = (uint)zip.READ_BITS_LSB(2, state);
run += 3;
code = last_code;
break;
case 17:
run = (uint)zip.READ_BITS_LSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run = (uint)zip.READ_BITS_LSB(3, state);
run += 3;
code = 0;
break;
case 18:
run = (uint)zip.READ_BITS_LSB(7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run = (uint)zip.READ_BITS_LSB(7, state);
run += 11;
code = 0;
break;
@@ -430,7 +366,7 @@ namespace LibMSPackSharp.Compression
zip.DISTANCE_len[i++] = 0;
}
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
zip.STORE_BITS(state);
return 0;
}
@@ -444,15 +380,15 @@ namespace LibMSPackSharp.Compression
Error err;
ushort sym;
zip.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left);
BufferState state = zip.RESTORE_BITS();
do
{
// Read in last block bit
last_block = (uint)zip.READ_BITS_LSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
last_block = (uint)zip.READ_BITS_LSB(1, state);
// Read in block type
block_type = (uint)zip.READ_BITS_LSB(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
block_type = (uint)zip.READ_BITS_LSB(2, state);
if (block_type == 0)
{
@@ -460,29 +396,29 @@ namespace LibMSPackSharp.Compression
byte[] lens_buf = new byte[4];
// Go to byte boundary
i = (uint)(bits_left & 7);
zip.REMOVE_BITS_LSB((int)i, ref bit_buffer, ref bits_left);
i = (uint)(state.BitsLeft & 7);
state.REMOVE_BITS_LSB((int)i);
// Read 4 bytes of data, emptying the bit-buffer if necessary
for (i = 0; (bits_left >= 8); i++)
for (i = 0; (state.BitsLeft >= 8); i++)
{
if (i == 4)
return Error.INF_ERR_BITBUF;
lens_buf[i] = (byte)zip.PEEK_BITS_LSB(8, bit_buffer);
zip.REMOVE_BITS_LSB(8, ref bit_buffer, ref bits_left);
lens_buf[i] = (byte)zip.PEEK_BITS_LSB(8, state.BitBuffer);
state.REMOVE_BITS_LSB(8);
}
if (bits_left != 0)
if (state.BitsLeft != 0)
return Error.INF_ERR_BITBUF;
while (i < 4)
{
zip.READ_IF_NEEDED(ref i_ptr, ref i_end);
zip.READ_IF_NEEDED(state);
if (zip.Error != Error.MSPACK_ERR_OK)
return zip.Error;
lens_buf[i++] = zip.InputBuffer[i_ptr++];
lens_buf[i++] = zip.InputBuffer[state.InputPointer++];
}
// Get the length and its complement
@@ -496,21 +432,21 @@ namespace LibMSPackSharp.Compression
// Read and copy the uncompressed data into the window
while (length > 0)
{
zip.READ_IF_NEEDED(ref i_ptr, ref i_end);
zip.READ_IF_NEEDED(state);
if (zip.Error != Error.MSPACK_ERR_OK)
return zip.Error;
this_run = length;
if (this_run > (uint)(i_end - i_ptr))
this_run = (uint)(i_end - i_ptr);
if (this_run > (uint)(state.InputEnd - state.InputPointer))
this_run = (uint)(state.InputEnd - state.InputPointer);
if (this_run > (MSZIP_FRAME_SIZE - zip.WindowPosition))
this_run = MSZIP_FRAME_SIZE - zip.WindowPosition;
Array.Copy(zip.InputBuffer, i_ptr, zip.Window, zip.WindowPosition, this_run);
Array.Copy(zip.InputBuffer, state.InputPointer, zip.Window, zip.WindowPosition, this_run);
zip.WindowPosition += this_run;
i_ptr += (int)this_run;
state.InputPointer += (int)this_run;
length -= this_run;
err = FLUSH_IF_NEEDED(zip);
@@ -555,12 +491,12 @@ namespace LibMSPackSharp.Compression
else
{
// Block with dynamic Huffman codes
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
zip.STORE_BITS(state);
if ((err = ReadLens(zip)) != Error.MSPACK_ERR_OK)
return err;
zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
state = zip.RESTORE_BITS();
}
// Now huffman lengths are read for either kind of block,
@@ -574,7 +510,7 @@ namespace LibMSPackSharp.Compression
// Decode forever until end of block code
for (; ; )
{
code = (uint)zip.READ_HUFFSYM_LSB(zip.LITERAL_table, zip.LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
code = (uint)zip.READ_HUFFSYM_LSB(zip.LITERAL_table, zip.LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS, state);
if (code < 256)
{
@@ -594,16 +530,16 @@ namespace LibMSPackSharp.Compression
if (code >= 29)
return Error.INF_ERR_LITCODE; // Codes 286-287 are illegal
length = (uint)zip.READ_BITS_T_LSB(lit_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
length += lit_lengths[code];
length = (uint)zip.READ_BITS_T_LSB(LiteralExtraBits[code], state);
length += LiteralLengths[code];
code = (uint)zip.READ_HUFFSYM_LSB(zip.DISTANCE_table, zip.DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
code = (uint)zip.READ_HUFFSYM_LSB(zip.DISTANCE_table, zip.DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS, state);
if (code >= 30)
return Error.INF_ERR_DISTCODE;
distance = (uint)zip.READ_BITS_T_LSB(dist_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
distance += dist_offsets[code];
distance = (uint)zip.READ_BITS_T_LSB(DistanceExtraBits[code], state);
distance += DistanceOffsets[code];
// Match position is window position minus distance. If distance
// is more than window position numerically, it must 'wrap
@@ -672,7 +608,7 @@ namespace LibMSPackSharp.Compression
return Error.INF_ERR_FLUSH;
}
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
zip.STORE_BITS(state);
// Return success
return Error.MSPACK_ERR_OK;

View File

@@ -11,6 +11,7 @@
*/
using System;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
@@ -21,7 +22,7 @@ namespace LibMSPackSharp.Compression
/// <summary>
/// 32kb history window
/// </summary>
public byte[] Window { get; set; } = new byte[MSZIP.MSZIP_FRAME_SIZE];
public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE];
/// <summary>
/// Offset within window
@@ -39,15 +40,15 @@ namespace LibMSPackSharp.Compression
#region Huffman code lengths
public byte[] LITERAL_len { get; set; } = new byte[MSZIP.MSZIP_LITERAL_MAXSYMBOLS];
public byte[] DISTANCE_len { get; set; } = new byte[MSZIP.MSZIP_DISTANCE_MAXSYMBOLS];
public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS];
public byte[] DISTANCE_len { get; set; } = new byte[MSZIP_DISTANCE_MAXSYMBOLS];
#endregion
#region Huffman decoding tables
public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP.MSZIP_LITERAL_TABLESIZE];
public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP.MSZIP_DISTANCE_TABLESIZE];
public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP_LITERAL_TABLESIZE];
public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP_DISTANCE_TABLESIZE];
#endregion
@@ -57,13 +58,13 @@ namespace LibMSPackSharp.Compression
public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
/// <inheritdoc/>
public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public override void READ_BYTES(BufferState state)
{
READ_IF_NEEDED(ref i_ptr, ref i_end);
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return;
INJECT_BITS_LSB(InputBuffer[i_ptr++], 8, ref bit_buffer, ref bits_left);
INJECT_BITS_LSB(InputBuffer[state.InputPointer++], 8, state);
}
}
}

View File

@@ -22,67 +22,12 @@
using System;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class QTM
{
public const int QTM_FRAME_SIZE = 32768;
/* Quantum static data tables:
*
* Quantum uses 'position slots' to represent match offsets. For every
* match, a small 'position slot' number and a small offset from that slot
* are encoded instead of one large offset.
*
* position_base[] is an index to the position slot bases
*
* extra_bits[] states how many bits of offset-from-base data is needed.
*
* length_base[] and length_extra[] are equivalent in function, but are
* used for encoding selector 6 (variable length match) match lengths,
* instead of match offsets.
*
* They are generated with the following code:
* uint i, offset;
* for (i = 0, offset = 0; i < 42; i++) {
* position_base[i] = offset;
* extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1;
* offset += 1 << extra_bits[i];
* }
* for (i = 0, offset = 0; i < 26; i++) {
* length_base[i] = offset;
* length_extra[i] = (i < 2 ? 0 : i - 2) >> 2;
* offset += 1 << length_extra[i];
* }
* length_base[26] = 254; length_extra[26] = 0;
*/
private static readonly uint[] position_base = new uint[42]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864
};
private static readonly byte[] extra_bits = new byte[42]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19
};
private static readonly byte[] length_base = new byte[27]
{
0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26,
30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254
};
private static readonly byte[] length_extra = new byte[27]
{
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
/// <summary>
/// allocates Quantum decompression state for decoding the given stream.
///
@@ -126,13 +71,17 @@ namespace LibMSPackSharp.Compression
HeaderRead = 0,
Error = Error.MSPACK_ERR_OK,
InputPointer = 0,
InputEnd = 0,
OutputPointer = 0,
OutputEnd = 0,
EndOfInput = 0,
BitsLeft = 0,
BitBuffer = 0,
BufferState = new BufferState()
{
InputPointer = 0,
InputEnd = 0,
BitBuffer = 0,
BitsLeft = 0,
}
};
// Initialise arithmetic coding models
@@ -214,7 +163,7 @@ namespace LibMSPackSharp.Compression
return Error.MSPACK_ERR_OK;
// Restore local state
qtm.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left);
BufferState state = qtm.RESTORE_BITS();
window = qtm.Window;
window_posn = qtm.WindowPosition;
frame_todo = qtm.FrameTODO;
@@ -230,7 +179,7 @@ namespace LibMSPackSharp.Compression
{
H = 0xFFFF;
L = 0;
C = (ushort)qtm.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
C = (ushort)qtm.READ_BITS_MSB(16, state);
qtm.HeaderRead = 1;
}
@@ -244,7 +193,7 @@ namespace LibMSPackSharp.Compression
while (window_posn < frame_end)
{
selector = GET_SYMBOL(qtm, qtm.Model7, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
selector = GET_SYMBOL(qtm, qtm.Model7, ref H, ref L, ref C, state);
if (selector < 4)
{
// Literal byte
@@ -269,7 +218,7 @@ namespace LibMSPackSharp.Compression
break;
}
sym = GET_SYMBOL(qtm, mdl, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
sym = GET_SYMBOL(qtm, mdl, ref H, ref L, ref C, state);
window[window_posn++] = (byte)sym;
frame_todo--;
}
@@ -280,29 +229,29 @@ namespace LibMSPackSharp.Compression
{
// Selector 4 = fixed length match (3 bytes)
case 4:
sym = GET_SYMBOL(qtm, qtm.Model4, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = (uint)(position_base[sym] + extra + 1);
sym = GET_SYMBOL(qtm, qtm.Model4, ref H, ref L, ref C, state);
extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
match_length = 3;
break;
// Selector 5 = fixed length match (4 bytes)
case 5:
sym = GET_SYMBOL(qtm, qtm.Model5, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = (uint)(position_base[sym] + extra + 1);
sym = GET_SYMBOL(qtm, qtm.Model5, ref H, ref L, ref C, state);
extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
match_length = 4;
break;
// Selector 6 = variable length match
case 6:
sym = GET_SYMBOL(qtm, qtm.Model6Len, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra = (int)qtm.READ_MANY_BITS_MSB(length_extra[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_length = length_base[sym] + extra + 5;
sym = GET_SYMBOL(qtm, qtm.Model6Len, ref H, ref L, ref C, state);
extra = (int)qtm.READ_MANY_BITS_MSB(QTMLengthExtra[sym], state);
match_length = QTMLengthBase[sym] + extra + 5;
sym = GET_SYMBOL(qtm, qtm.Model6, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = (uint)(position_base[sym] + extra + 1);
sym = GET_SYMBOL(qtm, qtm.Model6, ref H, ref L, ref C, state);
extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
break;
default:
@@ -422,15 +371,15 @@ namespace LibMSPackSharp.Compression
if (frame_todo == 0)
{
// Re-align input
if ((bits_left & 7) != 0)
qtm.REMOVE_BITS_MSB(bits_left & 7, ref bit_buffer, ref bits_left);
if ((state.BitsLeft & 7) != 0)
state.REMOVE_BITS_MSB(state.BitsLeft & 7);
// Special Quantum hack -- cabd.c injects a trailer byte to allow the
// decompressor to realign itself. CAB Quantum blocks, unlike LZX
// blocks, can have anything from 0 to 4 trailing null bytes.
do
{
i = (int)qtm.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
i = (int)qtm.READ_BITS_MSB(8, state);
} while (i != 0xFF);
qtm.HeaderRead = 0;
@@ -470,7 +419,7 @@ namespace LibMSPackSharp.Compression
}
// Store local state
qtm.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
qtm.STORE_BITS(state);
qtm.WindowPosition = window_posn;
qtm.FrameTODO = frame_todo;
qtm.High = H;
@@ -480,7 +429,7 @@ namespace LibMSPackSharp.Compression
return Error.MSPACK_ERR_OK;
}
private static ushort GET_SYMBOL(QTMDStream qtm, QTMDModel model, ref ushort H, ref ushort L, ref ushort C, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
private static ushort GET_SYMBOL(QTMDStream qtm, QTMDModel model, ref ushort H, ref ushort L, ref ushort C, BufferState state)
{
uint range = (uint)((H - L) & 0xFFFF) + 1;
ushort symf = (ushort)(((((C - L + 1) * model.Syms[0].CumulativeFrequency) - 1) / range) & 0xFFFF);
@@ -527,9 +476,9 @@ namespace LibMSPackSharp.Compression
L <<= 1;
H = (ushort)((H << 1) | 1);
qtm.ENSURE_BITS(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
C = (ushort)((C << 1) | (qtm.PEEK_BITS_MSB(1, bit_buffer)));
qtm.REMOVE_BITS_MSB(1, ref bit_buffer, ref bits_left);
qtm.ENSURE_BITS(1, state);
C = (ushort)((C << 1) | (qtm.PEEK_BITS_MSB(1, state.BitBuffer)));
state.REMOVE_BITS_MSB(1);
}
return temp;

View File

@@ -130,20 +130,20 @@ namespace LibMSPackSharp.Compression
public override Error HUFF_ERROR() => throw new NotImplementedException();
/// <inheritdoc/>
public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left)
public override void READ_BYTES(BufferState state)
{
READ_IF_NEEDED(ref i_ptr, ref i_end);
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return;
byte b0 = InputBuffer[i_ptr++];
byte b0 = InputBuffer[state.InputPointer++];
READ_IF_NEEDED(ref i_ptr, ref i_end);
READ_IF_NEEDED(state);
if (Error != Error.MSPACK_ERR_OK)
return;
byte b1 = InputBuffer[i_ptr++];
INJECT_BITS_MSB((b0 << 8) | b1, 16, ref bit_buffer, ref bits_left);
byte b1 = InputBuffer[state.InputPointer++];
INJECT_BITS_MSB((b0 << 8) | b1, 16, state);
}
}
}