diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs index d2976478..394566ff 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.MSCAB.cs @@ -5,10 +5,9 @@ using System.Linq; using System.Text; using BurnOutSharp.Tools; +/// namespace BurnOutSharp.FileType { - /// - // TODO: Add multi-cabinet reading public class MSCABCabinet { @@ -800,7 +799,7 @@ namespace BurnOutSharp.FileType decompressed = dataBlock.CompressedData; break; case CompressionType.TYPE_MSZIP: - decompressed = MSZIPBlock.Deserialize(dataBlock.CompressedData).DecompressBlock(dataBlock.UncompressedSize, lastDecompressed); + // TODO: UNIMPLEMENTED break; case CompressionType.TYPE_QUANTUM: // TODO: UNIMPLEMENTED diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs index 787c05fc..ec3986d6 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs @@ -1,7 +1,7 @@ -using System; -using System.Linq; -using ComponentAce.Compression.Libs.zlib; +using BurnOutSharp.Tools; +/// +/// namespace BurnOutSharp.FileType { /// @@ -9,7 +9,7 @@ namespace BurnOutSharp.FileType /// 2-byte MSZIP signature MUST consist of the bytes 0x43 and 0x4B. The MSZIP signature MUST be /// the first 2 bytes in the MSZIP block.The MSZIP signature is shown in the following packet diagram. /// - internal class MSZIPBlock + public class MSZIPBlock { #region Constants @@ -54,12 +54,6 @@ namespace BurnOutSharp.FileType #endregion - #region Static Properties - - public static ZStream DecompressionStream { get; set; } = new ZStream(); - - #endregion - #region Serialization public static MSZIPBlock Deserialize(byte[] data) @@ -70,74 +64,280 @@ namespace BurnOutSharp.FileType MSZIPBlock block = new MSZIPBlock(); int dataPtr = 0; - block.Signature = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + block.Signature = data.ReadUInt16(ref dataPtr); if (block.Signature != SignatureValue) return null; - block.Data = new byte[data.Length - 2]; - Array.Copy(data, dataPtr, block.Data, 0, data.Length - 2); - dataPtr += data.Length - 2; + block.Data = data.ReadBytes(ref dataPtr, data.Length - 2); return block; } #endregion + } - #region Public Functionality + #region Deflate Implementation + + /// + /// How the data are compressed + /// + public enum DeflateCompressionType : byte + { + /// + /// no compression + /// + NoCompression = 0b00, /// - /// Decompress a single block of MS-ZIP data + /// Compressed with fixed Huffman codes /// - public byte[] DecompressBlock(int decompressedSize, byte[] previousBytes = null) - { - if (Data == null || Data.Length == 0) - return null; + FixedHuffman = 0b01, - try - { - // The first block can use DeflateStream since it has no history - if (previousBytes == null) - { - // Setup the input - DecompressionStream = new ZStream(); - int initErr = DecompressionStream.inflateInit(); - if (initErr != zlibConst.Z_OK) - return null; - } + /// + /// Compressed with dynamic Huffman codes + /// + DynamicHuffman = 0b10, - // All n+1 blocks require the previous uncompressed data as a dictionary - else - { - // TODO: We need to force a dictionary setting - at this point, mode is 8 not 6 - - // Setup the dictionary - int dictErr = DecompressionStream.inflateSetDictionary(previousBytes, previousBytes.Length); - if (dictErr != zlibConst.Z_OK) - return null; - } - - // Setup the output - byte[] output = new byte[decompressedSize]; - DecompressionStream.next_out = output; - DecompressionStream.avail_out = decompressedSize; - - // Inflate the data -- 0x78, 0x9C is needed to trick zlib - DecompressionStream.next_in = new byte[] { 0x78, 0x9C }.Concat(Data).ToArray(); - DecompressionStream.next_in_index = 0; - DecompressionStream.avail_in = Data.Length + 2; - - int err = DecompressionStream.inflate(zlibConst.Z_FULL_FLUSH); - if (err != zlibConst.Z_OK) - return null; - - return output; - } - catch - { - return null; - } - } - - #endregion + /// + /// Reserved (error) + /// + Reserved = 0b11, } + + public class MSZIPDeflate + { + /* + 3.2.5. Compressed blocks (length and distance codes) + + As noted above, encoded data blocks in the "deflate" format + consist of sequences of symbols drawn from three conceptually + distinct alphabets: either literal bytes, from the alphabet of + byte values(0..255), or pairs, + where the length is drawn from(3..258) and the distance is + drawn from(1..32,768). In fact, the literal and length + alphabets are merged into a single alphabet(0..285), where + values 0..255 represent literal bytes, the value 256 indicates + end-of-block, and values 257..285 represent length codes + (possibly in conjunction with extra bits following the symbol + code) as follows: + + Extra Extra Extra + Code Bits Length(s) Code Bits Lengths Code Bits Length(s) + ---- ---- ------ ---- ---- ------- ---- ---- ------- + 257 0 3 267 1 15,16 277 4 67-82 + 258 0 4 268 1 17,18 278 4 83-98 + 259 0 5 269 2 19-22 279 4 99-114 + 260 0 6 270 2 23-26 280 4 115-130 + 261 0 7 271 2 27-30 281 5 131-162 + 262 0 8 272 2 31-34 282 5 163-194 + 263 0 9 273 3 35-42 283 5 195-226 + 264 0 10 274 3 43-50 284 5 227-257 + 265 1 11,12 275 3 51-58 285 0 258 + 266 1 13,14 276 3 59-66 + + The extra bits should be interpreted as a machine integer + stored with the most-significant bit first, e.g., bits 1110 + represent the value 14. + + Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- + 0 0 1 10 4 33-48 20 9 1025-1536 + 1 0 2 11 4 49-64 21 9 1537-2048 + 2 0 3 12 5 65-96 22 10 2049-3072 + 3 0 4 13 5 97-128 23 10 3073-4096 + 4 1 5,6 14 6 129-192 24 11 4097-6144 + 5 1 7,8 15 6 193-256 25 11 6145-8192 + 6 2 9-12 16 7 257-384 26 12 8193-12288 + 7 2 13-16 17 7 385-512 27 12 12289-16384 + 8 3 17-24 18 8 513-768 28 13 16385-24576 + 9 3 25-32 19 8 769-1024 29 13 24577-32768 + */ + } + + public class MSZIPDeflateBlock + { + /* + 3.2.3. Details of block format + + Each block of compressed data begins with 3 header bits + containing the following data: + + first bit BFINAL + next 2 bits BTYPE + + Note that the header bits do not necessarily begin on a byte + boundary, since a block does not necessarily occupy an integral + number of bytes. + + BFINAL is set if and only if this is the last block of the data + set. + + BTYPE specifies how the data are compressed, as follows: + + 00 - no compression + 01 - compressed with fixed Huffman codes + 10 - compressed with dynamic Huffman codes + 11 - reserved (error) + + The only difference between the two compressed cases is how the + Huffman codes for the literal/length and distance alphabets are + defined. + + In all cases, the decoding algorithm for the actual data is as + follows: + + do + read block header from input stream. + if stored with no compression + skip any remaining bits in current partially + processed byte + read LEN and NLEN (see next section) + copy LEN bytes of data to output + otherwise + if compressed with dynamic Huffman codes + read representation of code trees (see + subsection below) + loop (until end of block code recognized) + decode literal/length value from input stream + if value < 256 + copy value (literal byte) to output stream + otherwise + if value = end of block (256) + break from loop + otherwise (value = 257..285) + decode distance from input stream + + move backwards distance bytes in the output + stream, and copy length bytes from this + position to the output stream. + end loop + while not last block + + Note that a duplicated string reference may refer to a string + in a previous block; i.e., the backward distance may cross one + or more block boundaries. However a distance cannot refer past + the beginning of the output stream. (An application using a + preset dictionary might discard part of the output stream; a + distance can refer to that part of the output stream anyway) + Note also that the referenced string may overlap the current + position; for example, if the last 2 bytes decoded have values + X and Y, a string reference with + adds X,Y,X,Y,X to the output stream. + + We now specify each compression method in turn. + */ + } + + public class MSZIPNonCompressedBlock + { + /* + 3.2.4. Non-compressed blocks (BTYPE=00) + + Any bits of input up to the next byte boundary are ignored. + The rest of the block consists of the following information: + + 0 1 2 3 4... + +---+---+---+---+================================+ + | LEN | NLEN |... LEN bytes of literal data...| + +---+---+---+---+================================+ + + LEN is the number of data bytes in the block. NLEN is the + one's complement of LEN. + */ + } + + public class MSZIPFixedHuffmanCompressedBlock + { + /* + 3.2.6. Compression with fixed Huffman codes (BTYPE = 01) + + The Huffman codes for the two alphabets are fixed, and are not + represented explicitly in the data.The Huffman code lengths + for the literal / length alphabet are: + + Lit Value Bits Codes + -------- - --------- + 0 - 143 8 00110000 through + 10111111 + 144 - 255 9 110010000 through + 111111111 + 256 - 279 7 0000000 through + 0010111 + 280 - 287 8 11000000 through + 11000111 + */ + } + + public class MSZIPDynamicHuffmanCompressedBlock + { + /* + 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) + + The Huffman codes for the two alphabets appear in the block + immediately after the header bits and before the actual + compressed data, first the literal/length code and then the + distance code. Each code is defined by a sequence of code + lengths, as discussed in Paragraph 3.2.2, above. For even + greater compactness, the code length sequences themselves are + compressed using a Huffman code. The alphabet for code lengths + is as follows: + + 0 - 15: Represent code lengths of 0 - 15 + 16: 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) + 17: Repeat a code length of 0 for 3 - 10 times. + (3 bits of length) + 18: Repeat a code length of 0 for 11 - 138 times + (7 bits of length) + + A code length of 0 indicates that the corresponding symbol in + the literal/length or distance alphabet will not occur in the + block, and should not participate in the Huffman code + construction algorithm given earlier. If only one distance + code is used, it is encoded using one bit, not zero bits; in + this case there is a single code length of one, with one unused + code. One distance code of zero bits means that there are no + distance codes used at all (the data is all literals). + + We can now define the format of the block: + + 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) + 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) + 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + + + (HCLEN + 4) x 3 bits: code lengths for the code length + alphabet given just above, in the order: 16, 17, 18, + 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + + These code lengths are interpreted as 3-bit integers + (0-7); as above, a code length of 0 means the + corresponding symbol (literal/length or distance code + length) is not used. + + HLIT + 257 code lengths for the literal/length alphabet, + encoded using the code length Huffman code + + HDIST + 1 code lengths for the distance alphabet, + encoded using the code length Huffman code + + The actual compressed data of the block, + encoded using the literal/length and distance Huffman + codes + + The literal/length symbol 256 (end of data), + encoded using the literal/length Huffman code + + The code length repeat codes can cross from HLIT + 257 to the + HDIST + 1 code lengths. In other words, all code lengths form + a single sequence of HLIT + HDIST + 258 values. + */ + } + + #endregion }