Start fixing MSZIP decoding (nw)

This commit is contained in:
Matt Nadareski
2022-12-17 22:01:00 -08:00
parent f2521a0110
commit 546bd70418
4 changed files with 184 additions and 82 deletions

View File

@@ -195,8 +195,9 @@ namespace BurnOutSharp.Builder
folder.DataBlocks = new Dictionary<int, CFDATA>();
for (int i = 0; i < folder.DataCount; i++)
{
long dataBlockStart = data.Position;
CFDATA dataBlock = ParseDataBlock(data, header.DataReservedSize);
folder.DataBlocks[(int)folder.CabStartOffset] = dataBlock;
folder.DataBlocks[(int)dataBlockStart] = dataBlock;
}
data.Seek(currentPosition, SeekOrigin.Begin);

View File

@@ -8,6 +8,8 @@ namespace BurnOutSharp.Utilities
/// </summary>
public class BitStream
{
#region Instance Variables
/// <summary>
/// Underlying stream to read from
/// </summary>
@@ -23,6 +25,8 @@ namespace BurnOutSharp.Utilities
/// </summary>
private int _bitPosition;
#endregion
public BitStream(byte[] data)
{
_stream = new MemoryStream(data);
@@ -37,6 +41,8 @@ namespace BurnOutSharp.Utilities
_bitPosition = -1;
}
#region Reading
/// <summary>
/// Read an array of bits from the input
/// </summary>
@@ -49,7 +55,7 @@ namespace BurnOutSharp.Utilities
return null;
// If we have an invalid bit buffer
if (_bitBuffer == null || _bitPosition < 0 || _bitPosition >= 8)
if (_bitBuffer == null || _bitPosition < 0 || _bitPosition > 7)
RefreshBuffer();
// Create an array to hold the bits
@@ -74,50 +80,149 @@ namespace BurnOutSharp.Utilities
return bits;
}
#region Byte-Aligned Reads
/// <summary>
/// Read a byte-aligned byte from the input
/// </summary>
public byte ReadAlignedByte()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
byte value = _stream.ReadByteValue();
RefreshBuffer();
return value;
}
/// <summary>
/// Read an array of bytes from the input
/// </summary>
/// <param name="byteCount">Number of bytes to read</param>
/// <returns>Array representing the read bytes, null on error</returns>
public byte[] ReadBytes(int byteCount)
public byte[] ReadAlignedBytes(int byteCount)
{
// If we have an invalid byte count
if (byteCount <= 0)
return null;
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Get the corresponding bit array
BitArray bits = ReadBits(byteCount * 8);
if (bits == null)
return null;
// Create the byte array
byte[] bytes = new byte[byteCount];
// Initialize the loop variables
byte byt = 0; int bitNumber = 0, byteNumber = 0;
// Loop and build the byte array
for (int i = 0; i < bits.Length; i++)
{
// Add the new bit to the byte
byt <<= 1;
byt |= (byte)(bits[i] ? 1 : 0);
bitNumber++;
// If we are at the end of a byte
if (bitNumber == 8)
{
bytes[byteNumber++] = byt;
bitNumber = 0;
}
}
// Add the last byte
bytes[byteNumber] = byt;
return bytes;
// Read the value and refreah the buffer
byte[] value = _stream.ReadBytes(byteCount);
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned sbyte from the input
/// </summary>
public sbyte ReadAlignedSByte()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
sbyte value = _stream.ReadSByte();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned short from the input
/// </summary>
public short ReadAlignedInt16()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
short value = _stream.ReadInt16();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned ushort from the input
/// </summary>
public ushort ReadAlignedUInt16()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
ushort value = _stream.ReadUInt16();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned int from the input
/// </summary>
public int ReadAlignedInt32()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
int value = _stream.ReadInt32();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned uint from the input
/// </summary>
public uint ReadAlignedUInt32()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
uint value = _stream.ReadUInt32();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned long from the input
/// </summary>
public long ReadAlignedInt64()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
long value = _stream.ReadInt64();
RefreshBuffer();
return value;
}
/// <summary>
/// Read a byte-aligned ulong from the input
/// </summary>
public ulong ReadAlignedUInt64()
{
// Read back a single byte
if (_stream.Position > 0)
_stream.Seek(-1, SeekOrigin.Current);
// Read the value and refreah the buffer
ulong value = _stream.ReadUInt64();
RefreshBuffer();
return value;
}
#endregion
#endregion
/// <summary>
/// Discard the remaining bits in the buffer
/// </summary>

View File

@@ -40,7 +40,7 @@ namespace BurnOutSharp.Utilities
byte value = 0;
int maxValue = Math.Min(8, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (byte)(array[i] ? 1 : 0);
@@ -56,8 +56,8 @@ namespace BurnOutSharp.Utilities
{
sbyte value = 0;
int maxValue = Math.Min(8, array.Length);
for (int i = 0; i < maxValue; i++)
int maxValue = Math.Min(val1: 8, array.Length);
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (sbyte)(array[i] ? 1 : 0);
@@ -74,7 +74,7 @@ namespace BurnOutSharp.Utilities
short value = 0;
int maxValue = Math.Min(16, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (short)(array[i] ? 1 : 0);
@@ -91,7 +91,7 @@ namespace BurnOutSharp.Utilities
ushort value = 0;
int maxValue = Math.Min(16, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (ushort)(array[i] ? 1 : 0);
@@ -108,7 +108,7 @@ namespace BurnOutSharp.Utilities
int value = 0;
int maxValue = Math.Min(32, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (int)(array[i] ? 1 : 0);
@@ -125,7 +125,7 @@ namespace BurnOutSharp.Utilities
uint value = 0;
int maxValue = Math.Min(32, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (uint)(array[i] ? 1 : 0);
@@ -142,7 +142,7 @@ namespace BurnOutSharp.Utilities
long value = 0;
int maxValue = Math.Min(64, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (long)(array[i] ? 1 : 0);
@@ -159,7 +159,7 @@ namespace BurnOutSharp.Utilities
ulong value = 0;
int maxValue = Math.Min(64, array.Length);
for (int i = 0; i < maxValue; i++)
for (int i = maxValue - 1; i >= 0; i--)
{
value <<= 1;
value |= (ulong)(array[i] ? 1 : 0);

View File

@@ -371,7 +371,7 @@ namespace BurnOutSharp.Wrappers
var header = new Models.MicrosoftCabinet.MSZIP.BlockHeader();
header.Signature = data.ReadBits(2 * 8).AsUInt16();
header.Signature = data.ReadAlignedUInt16();
if (header.Signature != 0x4B43)
return null;
@@ -419,7 +419,7 @@ namespace BurnOutSharp.Wrappers
byte HDIST = (byte)(data.ReadBits(5).AsByte() + 1);
// HCLEN, # of Code Length codes - 4
byte HCLEN = (byte)(data.ReadBits(5).AsByte() + 4);
byte HCLEN = (byte)(data.ReadBits(4).AsByte() + 4);
// (HCLEN + 4) x 3 bits: code lengths for the code length
// alphabet given just above
@@ -428,23 +428,20 @@ namespace BurnOutSharp.Wrappers
// (0-7); as above, a code length of 0 means the
// corresponding symbol (literal/ length or distance code
// length) is not used.
int[] codeLengthAlphabet = new int[19];
int[] bitLengths = new int[19];
for (ulong i = 0; i < HCLEN; i++)
codeLengthAlphabet[BitLengthOrder[i]] = data.ReadBits(3).AsByte();
for (ulong i = HCLEN; i < 19; i++)
codeLengthAlphabet[BitLengthOrder[i]] = 0;
bitLengths[BitLengthOrder[i]] = data.ReadBits(3).AsByte();
// Code length Huffman code
int[] codeLengthHuffmanCode = CreateTable(codeLengthAlphabet);
int[] bitLengthTable = CreateTable(bitLengths);
// HLIT + 257 code lengths for the literal/length alphabet,
// encoded using the code length Huffman code
header.LiteralLengths = BuildHuffmanTree(data, HLIT, codeLengthHuffmanCode);
header.LiteralLengths = BuildHuffmanTree(data, HLIT, bitLengthTable);
// HDIST + 1 code lengths for the distance alphabet,
// encoded using the code length Huffman code
header.DistanceCodes = BuildHuffmanTree(data, HDIST, codeLengthHuffmanCode);
header.DistanceCodes = BuildHuffmanTree(data, HDIST, bitLengthTable);
return header;
}
@@ -463,8 +460,8 @@ namespace BurnOutSharp.Wrappers
var header = new Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader();
header.LEN = data.ReadBits(2 * 8).AsUInt16();
header.NLEN = data.ReadBits(2 * 8).AsUInt16();
header.LEN = data.ReadAlignedUInt16();
header.NLEN = data.ReadAlignedUInt16();
// TODO: Confirm NLEN is 1's compliment of LEN
return header;
@@ -486,41 +483,43 @@ namespace BurnOutSharp.Wrappers
int lastCode = 0, repeatLength = 0;
for (ulong i = 0; i < codeCount; i++)
{
int code = codeLengths[(int)data.ReadBits(7).AsUInt64()];
int codeLength = codeLengths[data.ReadBits(7).AsUInt16()];
if (codeLengths[codeLength] > 7)
_ = data.ReadBits(codeLengths[codeLength] - 7);
// Represent code lengths of 0 - 15
if (code > 0 && code <= 15)
if (codeLength > 0 && codeLength <= 15)
{
lastCode = code;
tree[i] = code;
lastCode = codeLength;
tree[i] = codeLength;
}
// Copy the previous code length 3 - 6 times.
// The next 2 bits indicate repeat length (0 = 3, ... , 3 = 6)
// Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will expand to 12 code lengths of 8 (1 + 6 + 5)
else if (code == 16)
// Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will expand to 12 code lengths of 8 (1 + 6 + 5)
else if (codeLength == 16)
{
repeatLength = (int)data.ReadBits(2).AsUInt64();
repeatLength = data.ReadBits(2).AsByte();
repeatLength += 2;
code = lastCode;
codeLength = lastCode;
}
// Repeat a code length of 0 for 3 - 10 times.
// (3 bits of length)
else if (code == 17)
else if (codeLength == 17)
{
repeatLength = (int)data.ReadBits(3).AsUInt64();
repeatLength = data.ReadBits(3).AsByte();
repeatLength += 3;
code = 0;
codeLength = 0;
}
// Repeat a code length of 0 for 11 - 138 times
// (7 bits of length)
else if (code == 18)
else if (codeLength == 18)
{
repeatLength = (int)data.ReadBits(7).AsUInt64();
repeatLength = data.ReadBits(7).AsByte();
repeatLength += 11;
code = 0;
codeLength = 0;
}
// Everything else
@@ -532,7 +531,7 @@ namespace BurnOutSharp.Wrappers
// If we had a repeat length
for (; repeatLength > 0; repeatLength--)
{
tree[i++] = code;
tree[i++] = codeLength;
}
}
@@ -554,18 +553,15 @@ namespace BurnOutSharp.Wrappers
{
// Count the number of codes for each code length. Let
// bl_count[N] be the number of codes of length N, N >= 1.
var bl_count = new Dictionary<int, int>();
int[] bl_count = new int[259];
for (int i = 0; i < lengths.Length; i++)
{
if (!bl_count.ContainsKey(lengths[i]))
bl_count[lengths[i]] = 0;
bl_count[lengths[i]]++;
}
// Find the numerical value of the smallest code for each
// code length:
var next_code = new Dictionary<int, int>();
// 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++)
@@ -580,7 +576,7 @@ namespace BurnOutSharp.Wrappers
// (which have a bit length of zero) must not be assigned a
// value.
int[] distances = new int[lengths.Length];
for (int n = 0; n <= lengths.Length; n++)
for (int n = 0; n < lengths.Length; n++)
{
int len = lengths[n];
if (len != 0)
@@ -702,7 +698,7 @@ namespace BurnOutSharp.Wrappers
// Copy LEN bytes of data to output
var header = deflateBlockHeader.BlockDataHeader as Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader;
ushort length = header.LEN;
decodedBytes.AddRange(dataStream.ReadBytes(length));
decodedBytes.AddRange(dataStream.ReadAlignedBytes(length));
}
// Otherwise
@@ -726,7 +722,7 @@ namespace BurnOutSharp.Wrappers
while (true)
{
// Decode literal/length value from input stream
int symbol = literalDecodeTable[dataStream.ReadBits(9).AsUInt64()];
int symbol = literalDecodeTable[dataStream.ReadBits(9).AsUInt16()];
// Copy value (literal byte) to output stream
if (symbol < 256)