// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // SevenZip LZMA SDK: http://www.7-zip.org/download.html // Usage: http://stackoverflow.com/questions/7646328/how-to-use-the-7z-sdk-to-compress-and-decompress-a-file using SevenZip; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Text; using System.Threading; namespace WPinternals { internal static class LZMA { internal static byte[] Decompress(byte[] Input, UInt32 Offset, UInt32 InputSize) { byte[] Properties = new byte[5]; Buffer.BlockCopy(Input, (int)Offset, Properties, 0, 5); UInt64 OutputSize = ByteOperations.ReadUInt64(Input, Offset + 5); SevenZip.Compression.LZMA.Decoder Coder = new SevenZip.Compression.LZMA.Decoder(); Coder.SetDecoderProperties(Properties); MemoryStream InStream = new MemoryStream(Input, (int)Offset + 0x0D, (int)InputSize - 0x0D); byte[] Output = new byte[OutputSize]; MemoryStream OutStream = new MemoryStream(Output, true); Coder.Code(InStream, OutStream, (Int64)InputSize - 0x0D, (Int64)OutputSize, null); OutStream.Flush(); OutStream.Close(); InStream.Close(); return Output; } internal static byte[] Compress(byte[] Input, UInt32 Offset, UInt32 InputSize) { SevenZip.Compression.LZMA.Encoder Coder = new SevenZip.Compression.LZMA.Encoder(); MemoryStream InStream = new MemoryStream(Input, (int)Offset, (int)InputSize); MemoryStream OutStream = new MemoryStream(); // Write the encoder properties Coder.WriteCoderProperties(OutStream); // Write the decompressed file size OutStream.Write(BitConverter.GetBytes(InStream.Length), 0, 8); // Encode the file Coder.Code(InStream, OutStream, (Int64)InputSize, -1, null); byte[] Output = new byte[OutStream.Length]; Buffer.BlockCopy(OutStream.GetBuffer(), 0, Output, 0, (int)OutStream.Length); OutStream.Flush(); OutStream.Close(); InStream.Close(); return Output; } } public class LZMACompressionStream : Stream { private SevenZip.Compression.LZMA.Encoder Encoder = null; private SevenZip.Compression.LZMA.Decoder Decoder = null; private PumpStream BufferStream; private Stream stream; private bool LeaveOpen; private Thread WorkThread; private CancellationTokenSource source; private CancellationToken token; public LZMACompressionStream(Stream stream, CompressionMode mode, bool LeaveOpen, int DictionarySize, int PosStateBits, int LitContextBits, int LitPosBits, int Algorithm, int NumFastBytes, string MatchFinder, bool EndMarker) { this.stream = stream; this.LeaveOpen = LeaveOpen; BufferStream = new PumpStream(); source = new CancellationTokenSource(); token = source.Token; if (mode == CompressionMode.Compress) { Encoder = new SevenZip.Compression.LZMA.Encoder(); if (DictionarySize != 0) Encoder.SetCoderProperties( new SevenZip.CoderPropID[8] {CoderPropID.DictionarySize, CoderPropID.PosStateBits, CoderPropID.LitContextBits, CoderPropID.LitPosBits, CoderPropID.Algorithm, CoderPropID.NumFastBytes, CoderPropID.MatchFinder, CoderPropID.EndMarker}, new object[8] { DictionarySize, PosStateBits, LitContextBits, LitPosBits, Algorithm, NumFastBytes, MatchFinder, EndMarker }); Encoder.WriteCoderProperties(stream); WorkThread = new Thread(new ThreadStart(Encode)); } else { byte[] DecoderProperties = new byte[5]; stream.Read(DecoderProperties, 0, 5); Decoder = new SevenZip.Compression.LZMA.Decoder(); Decoder.SetDecoderProperties(DecoderProperties); WorkThread = new Thread(new ThreadStart(Decode)); } WorkThread.Start(); } public LZMACompressionStream(Stream stream, CompressionMode mode, bool LeaveOpen) : this(stream, mode, LeaveOpen, 0, 0, 0, 0, 0, 0, null, false) { } private void Encode() { Encoder.Code(BufferStream, stream, -1, -1, null, token); if (LeaveOpen == false) stream.Close(); } private void Decode() { Decoder.Code(stream, BufferStream, -1, -1, null, token); BufferStream.Close(); if (LeaveOpen == false) stream.Close(); } public override void Close() { if (Encoder != null) BufferStream.Close(); else if (WorkThread.IsAlive) { if (source != null) source.Cancel(); WorkThread.Join(); } } public override int Read(byte[] buffer, int offset, int count) { return BufferStream.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { BufferStream.Write(buffer, offset, count); } public override bool CanRead { get { return (Decoder != null); } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return (Encoder != null); } } public override void Flush() { } public override long Length { get { return 0; } } public override long Position { get { return 0; } set { } } public override long Seek(long offset, SeekOrigin origin) { return 0; } public override void SetLength(long value) { } } public class PumpStream : Stream { private Queue BufferQueue; private int BufferOffset; private long MaxBufferSize; private long BufferSize; private bool Closed; private bool EOF; public PumpStream(long MaxBufferSize, int ReadTimeout, int WriteTimeout) { this.MaxBufferSize = MaxBufferSize; this.ReadTimeout = ReadTimeout; this.WriteTimeout = WriteTimeout; BufferQueue = new Queue(); BufferOffset = 0; BufferSize = 0; Closed = false; EOF = false; } public PumpStream() : this(16777216, Timeout.Infinite, Timeout.Infinite) { } public new void Dispose() { BufferQueue.Clear(); } public override void Close() { Closed = true; lock (BufferQueue) Monitor.Pulse(BufferQueue); } public override int Read(byte[] buffer, int offset, int count) { int BytesRead = 0; lock (BufferQueue) { while (BytesRead < count && EOF == false) { if (BufferQueue.Count > 0) { byte[] b = BufferQueue.Peek(); if ((b.Length - BufferOffset) <= (count - BytesRead)) { Array.Copy(b, BufferOffset, buffer, offset + BytesRead, (b.Length - BufferOffset)); BufferQueue.Dequeue(); BufferSize -= b.Length; Monitor.Pulse(BufferQueue); BytesRead += (b.Length - BufferOffset); BufferOffset = 0; } else { Array.Copy(b, BufferOffset, buffer, offset + BytesRead, (count - BytesRead)); BufferOffset += (count - BytesRead); BytesRead += (count - BytesRead); } } else { if (Closed == false) { if (Monitor.Wait(BufferQueue, ReadTimeout) == false) throw new IOException("Could not read from stream: Timeout expired waiting for data to be written."); } else EOF = true; } } } return BytesRead; } public override void Write(byte[] buffer, int offset, int count) { lock (BufferQueue) { while (BufferSize >= MaxBufferSize) if (Monitor.Wait(BufferQueue, WriteTimeout) == false) throw new IOException("Could not write to stream: Timeout expired waiting for data to be read."); byte[] b = new byte[count]; Array.Copy(buffer, offset, b, 0, count); BufferQueue.Enqueue(b); BufferSize += b.Length; Monitor.Pulse(BufferQueue); } } public override int ReadTimeout { get; set; } public override int WriteTimeout { get; set; } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override void Flush() { } public override long Length { get { return 0; } } public override long Position { get { return 0; } set { } } public override long Seek(long offset, SeekOrigin origin) { return 0; } public override void SetLength(long value) { } } }