Handle model issues, combine logic

This commit is contained in:
Matt Nadareski
2023-09-22 11:53:27 -04:00
parent 297fffe8d7
commit 8174af616f
2 changed files with 196 additions and 98 deletions

View File

@@ -7,9 +7,8 @@ using static SabreTools.Compression.MSZIP.Constants;
namespace SabreTools.Compression.MSZIP
{
// TODO: Combine parts of the fixed and dynamic implementation
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class Decompressor
public class DeflateDecompressor
{
/// <summary>
/// Internal bitstream to use for decompression
@@ -20,7 +19,7 @@ namespace SabreTools.Compression.MSZIP
/// Create a new Decompressor from a Stream
/// </summary>
/// <param name="input">Stream to decompress</param>
public Decompressor(Stream input)
public DeflateDecompressor(Stream input)
{
// If we have an invalid stream
if (input == null || !input.CanRead || !input.CanSeek)
@@ -116,6 +115,52 @@ namespace SabreTools.Compression.MSZIP
return header;
}
/// <summary>
/// Read a FixedHuffmanCompressedBlockHeader from the input stream
/// </summary>
private FixedHuffmanCompressedBlockHeader ReadFixedHuffmanCompressedBlockHeader()
{
return new FixedHuffmanCompressedBlockHeader();
}
/// <summary>
/// Read a DynamicHuffmanCompressedBlockHeader from the input stream
/// </summary>
private (DynamicHuffmanCompressedBlockHeader, uint, uint) ReadDynamicHuffmanCompressedBlockHeader()
{
var header = new DynamicHuffmanCompressedBlockHeader();
// Setup the counts first
uint numLiteral = 257 + _bitStream.ReadBitsLSB(5) ?? 0;
uint numDistance = 1 + _bitStream.ReadBitsLSB(5) ?? 0;
uint numLength = 4 + _bitStream.ReadBitsLSB(4) ?? 0;
// Convert the alphabet based on lengths
uint[] lengthLengths = new uint[19];
for (int i = 0; i < numLength; i++)
{
lengthLengths[FixedAlphabet[i]] = (byte)_bitStream.ReadBitsLSB(3);
}
for (int i = (int)numLength; i < 19; i++)
{
lengthLengths[FixedAlphabet[i]] = 0;
}
// Make the lengths tree
HuffmanDecoder lengthTree = new HuffmanDecoder(lengthLengths, 19);
// Setup the literal and distance lengths
header.LiteralLengths = new int[288];
header.DistanceCodes = new int[32];
// Read the literal and distance codes
int repeatCode = 1;
uint leftover = ReadHuffmanLengths(lengthTree, header.LiteralLengths, numLiteral, 0, ref repeatCode);
_ = ReadHuffmanLengths(lengthTree, header.DistanceCodes, numDistance, leftover, ref repeatCode);
return (header, numLiteral, numDistance);
}
#endregion
#region Data
@@ -144,65 +189,15 @@ namespace SabreTools.Compression.MSZIP
{
var bytes = new List<byte>();
while (true)
{
// Read the next symbol from the bitstream
uint? sym = _bitStream.ReadBitsMSB(7);
if (sym == null)
return null;
// Get the fixed huffman header
var header = ReadFixedHuffmanCompressedBlockHeader();
// If we have a symbol number <= 23, adjust it
if (sym <= 23)
{
sym += 256;
}
// Make the literal and distance trees
HuffmanDecoder literalTree = new HuffmanDecoder(header.LiteralLengths, 288);
HuffmanDecoder distanceTree = new HuffmanDecoder(header.DistanceCodes, 30);
// Read the next bit otherwise
else
{
sym = (sym << 1) + _bitStream.ReadBit();
if (sym <= 191)
sym -= 48;
else if (sym <= 199)
sym += 88;
else
sym = (sym << 1) + _bitStream.ReadBit() - 256;
}
// If we have an immediate symbol
if (sym < 256)
{
bytes.Add((byte)sym);
}
// If we have the ending symbol
else if (sym == 256)
{
break;
}
// If we have a length/distance pair
else
{
sym -= 257;
uint? length = MatchLengths[(int)sym] + _bitStream.ReadBitsLSB(MatchExtraBits[(int)sym]);
if (length == null)
return null;
uint? distanceCode = _bitStream.ReadBitsMSB(5);
if (distanceCode == null)
return null;
uint? distance = DistanceLengths[(int)distanceCode] + _bitStream.ReadBitsLSB(DistanceExtraBits[(int)distanceCode]);
if (distance == null)
return null;
byte[] arr = bytes.Skip(bytes.Count - (int)distance).Take((int)length).ToArray();
bytes.AddRange(arr);
}
}
return bytes.ToArray();
// Now loop and decode
return ReadHuffmanBlock(literalTree, distanceTree);
}
/// <summary>
@@ -210,38 +205,22 @@ namespace SabreTools.Compression.MSZIP
/// </summary>
private byte[] ReadDynamicHuffman()
{
// Setup the counts first
uint numLiteral = 257 + _bitStream.ReadBitsLSB(5) ?? 0;
uint numDistance = 1 + _bitStream.ReadBitsLSB(5) ?? 0;
uint numLength = 4 + _bitStream.ReadBitsLSB(4) ?? 0;
// Convert the alphabet based on lengths
byte[] lengthLengths = new byte[19];
for (int i = 0; i < numLength; i++)
{
lengthLengths[FixedAlphabet[i]] = (byte)_bitStream.ReadBitsLSB(3);
}
for (int i = (int)numLength; i < 19; i++)
{
lengthLengths[FixedAlphabet[i]] = 0;
}
// Make the lengths tree
HuffmanDecoder lengthTree = new HuffmanDecoder(lengthLengths, 19);
// Setup the literal and distance lengths
byte[] literalLengths = new byte[288];
byte[] distanceLengths = new byte[32];
// Read the literal and distance codes
int repeatCode = 1;
uint leftover = ReadHuffmanLengths(lengthTree, literalLengths, numLiteral, 0, ref repeatCode);
_ = ReadHuffmanLengths(lengthTree, distanceLengths, numDistance, leftover, ref repeatCode);
// Get the dynamic huffman header
(var header, uint numLiteral, uint numDistance) = ReadDynamicHuffmanCompressedBlockHeader();
// Make the literal and distance trees
HuffmanDecoder literalTree = new HuffmanDecoder(literalLengths, numLiteral);
HuffmanDecoder distanceTree = new HuffmanDecoder(distanceLengths, numDistance);
HuffmanDecoder literalTree = new HuffmanDecoder(header.LiteralLengths, numLiteral);
HuffmanDecoder distanceTree = new HuffmanDecoder(header.DistanceCodes, numDistance);
// Now loop and decode
return ReadHuffmanBlock(literalTree, distanceTree);
}
/// <summary>
/// Read an RFC1951 block with Huffman compression
/// </summary>
private byte[] ReadHuffmanBlock(HuffmanDecoder literalTree, HuffmanDecoder distanceTree)
{
// Now loop and decode
var bytes = new List<byte>();
while (true)
@@ -287,7 +266,66 @@ namespace SabreTools.Compression.MSZIP
/// <summary>
/// Read the huffman lengths
/// </summary>
private uint ReadHuffmanLengths(HuffmanDecoder lengthTree, byte[] lengths, uint numCodes, uint repeat, ref int repeatCode)
private uint ReadHuffmanLengths(HuffmanDecoder lengthTree, int[] lengths, uint numCodes, uint repeat, ref int repeatCode)
{
int i = 0;
// First fill in any repeat codes
while (repeat > 0)
{
lengths[i++] = (byte)repeatCode;
repeat--;
}
// Then process the rest of the table
while (i < numCodes)
{
// Get the next length encoding from the stream
int lengthEncoding = lengthTree.Decode(_bitStream);
// Values less than 16 are encoded directly
if (lengthEncoding < 16)
{
lengths[i++] = (byte)lengthEncoding;
repeatCode = lengthEncoding;
}
// Otherwise, the repeat count is based on the next values
else
{
// Determine the repeat count and code from the encoding
if (lengthEncoding == 16)
{
repeat = 3 + _bitStream.ReadBitsLSB(2) ?? 0;
}
else if (lengthEncoding == 17)
{
repeat = 3 + _bitStream.ReadBitsLSB(3) ?? 0;
repeatCode = 0;
}
else if (lengthEncoding == 18)
{
repeat = 11 + _bitStream.ReadBitsLSB(7) ?? 0;
repeatCode = 0;
}
// Read in the expected lengths
while (i < numCodes && repeat > 0)
{
lengths[i++] = (byte)repeatCode;
repeat--;
}
}
}
// Return any repeat value we have left over
return repeat;
}
/// <summary>
/// Read the huffman lengths
/// </summary>
private uint ReadHuffmanLengths(HuffmanDecoder lengthTree, uint[] lengths, uint numCodes, uint repeat, ref int repeatCode)
{
int i = 0;

View File

@@ -15,19 +15,19 @@ namespace SabreTools.Compression.MSZIP
/// </summary>
/// <param name="lengths">Array representing the number of bits for each value</param>
/// <param name="numCodes">Number of Huffman codes encoded</param>
public HuffmanDecoder(byte[] lengths, uint numCodes)
public HuffmanDecoder(int[] lengths, uint numCodes)
{
// Set the root to null for now
_root = null;
// Determine the value for max_bits
int max_bits = lengths.Max();
uint max_bits = (uint)lengths.Max();
// Count the number of codes for each code length
int[] bl_count = new int[max_bits + 1];
for (int i = 0; i < numCodes; i++)
{
int length = lengths[i];
uint length = (uint)lengths[i];
bl_count[length]++;
}
@@ -48,7 +48,7 @@ namespace SabreTools.Compression.MSZIP
int[] tree = new int[numCodes];
for (int i = 0; i < numCodes; i++)
{
byte len = lengths[i];
uint len = (uint)lengths[i];
if (len == 0)
continue;
@@ -61,7 +61,67 @@ namespace SabreTools.Compression.MSZIP
for (int i = 0; i < numCodes; i++)
{
// If we have a 0-length code
byte len = lengths[i];
uint len = (uint)lengths[i];
if (len == 0)
continue;
// Insert the value starting at the root
_root = Insert(_root, i, len, tree[i]);
}
}
/// <summary>
/// Create a Huffman tree to decode with
/// </summary>
/// <param name="lengths">Array representing the number of bits for each value</param>
/// <param name="numCodes">Number of Huffman codes encoded</param>
public HuffmanDecoder(uint[] lengths, uint numCodes)
{
// Set the root to null for now
_root = null;
// Determine the value for max_bits
uint max_bits = lengths.Max();
// Count the number of codes for each code length
int[] bl_count = new int[max_bits + 1];
for (int i = 0; i < numCodes; i++)
{
uint length = lengths[i];
bl_count[length]++;
}
// Find the numerical value of the smalles code for each code length
int[] next_code = new int[max_bits + 1];
int code = 0;
bl_count[0] = 0;
for (int bits = 1; bits <= max_bits; bits++)
{
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
// Assign numerical values to all codes, using consecutive
// values for all codes of the same length with the base
// values determined at step 2. Codes that are never used
// (which have a bit length of zero) must not be assigned a value.
int[] tree = new int[numCodes];
for (int i = 0; i < numCodes; i++)
{
uint len = lengths[i];
if (len == 0)
continue;
// Set the value in the tree
tree[i] = next_code[len];
next_code[len]++;
}
// Now insert the values into the structure
for (int i = 0; i < numCodes; i++)
{
// If we have a 0-length code
uint len = lengths[i];
if (len == 0)
continue;
@@ -106,9 +166,9 @@ namespace SabreTools.Compression.MSZIP
/// <param name="code">Encoding of the value to traverse</param>
/// <returns>New instance of the node with value appended</returns>
#if NET48
private static HuffmanNode Insert(HuffmanNode node, int value, int length, int code)
private static HuffmanNode Insert(HuffmanNode node, int value, uint length, int code)
#else
private static HuffmanNode Insert(HuffmanNode? node, int value, int length, int code)
private static HuffmanNode Insert(HuffmanNode? node, int value, uint length, int code)
#endif
{
// If no node is provided, create a new one
@@ -123,7 +183,7 @@ namespace SabreTools.Compression.MSZIP
}
// Otherwise, get the next bit from the code
byte nextBit = (byte)(code >> (length - 1) & 1);
byte nextBit = (byte)(code >> (int)(length - 1) & 1);
// Left == 0, Right == 1
if (nextBit == 0)