mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-09 02:16:46 +00:00
Start fixing MSZIP decoding (nw)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user