diff --git a/BurnOutSharp.Builder/MicrosoftCabinet.cs b/BurnOutSharp.Builder/MicrosoftCabinet.cs index af5139bd..79cc6d4c 100644 --- a/BurnOutSharp.Builder/MicrosoftCabinet.cs +++ b/BurnOutSharp.Builder/MicrosoftCabinet.cs @@ -195,8 +195,9 @@ namespace BurnOutSharp.Builder folder.DataBlocks = new Dictionary(); 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); diff --git a/BurnOutSharp.Utilities/BitStream.cs b/BurnOutSharp.Utilities/BitStream.cs index 33e046c4..004e3a37 100644 --- a/BurnOutSharp.Utilities/BitStream.cs +++ b/BurnOutSharp.Utilities/BitStream.cs @@ -8,6 +8,8 @@ namespace BurnOutSharp.Utilities /// public class BitStream { + #region Instance Variables + /// /// Underlying stream to read from /// @@ -23,6 +25,8 @@ namespace BurnOutSharp.Utilities /// private int _bitPosition; + #endregion + public BitStream(byte[] data) { _stream = new MemoryStream(data); @@ -37,6 +41,8 @@ namespace BurnOutSharp.Utilities _bitPosition = -1; } + #region Reading + /// /// Read an array of bits from the input /// @@ -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 + + /// + /// Read a byte-aligned byte from the input + /// + 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; + } + /// /// Read an array of bytes from the input /// /// Number of bytes to read /// Array representing the read bytes, null on error - 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; } + /// + /// Read a byte-aligned sbyte from the input + /// + 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; + } + + /// + /// Read a byte-aligned short from the input + /// + 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; + } + + /// + /// Read a byte-aligned ushort from the input + /// + 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; + } + + /// + /// Read a byte-aligned int from the input + /// + 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; + } + + /// + /// Read a byte-aligned uint from the input + /// + 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; + } + + /// + /// Read a byte-aligned long from the input + /// + 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; + } + + /// + /// Read a byte-aligned ulong from the input + /// + 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 + /// /// Discard the remaining bits in the buffer /// diff --git a/BurnOutSharp.Utilities/Extensions.cs b/BurnOutSharp.Utilities/Extensions.cs index 3a5ed2b0..9efc42a7 100644 --- a/BurnOutSharp.Utilities/Extensions.cs +++ b/BurnOutSharp.Utilities/Extensions.cs @@ -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); diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs index c059b46d..541f915b 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs @@ -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[] 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(); + // 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)