public static bool DecompressFileLZMA(string inFile, string outFile) { try { SevenZip.Sdk.Compression.Lzma.Decoder coder = new SevenZip.Sdk.Compression.Lzma.Decoder(); FileStream input = new FileStream(inFile, FileMode.Open); FileStream output = new FileStream(outFile, FileMode.Create); // Read the decoder properties byte[] properties = new byte[5]; input.Read(properties, 0, 5); // Read in the decompress file size. byte[] fileLengthBytes = new byte[8]; input.Read(fileLengthBytes, 0, 8); long fileLength = BitConverter.ToInt64(fileLengthBytes, 0); coder.SetDecoderProperties(properties); coder.Code(input, output, input.Length, fileLength, null); output.Flush(); output.Close(); } catch (Exception) { return false; } return true; }
public Stream GetLumpStream(LumpType type) { var info = GetLumpInfo(type); if (info.UncompressedSize == 0) { return(GetSubStream(info.Offset, info.Length)); } using (var stream = GetSubStream(info.Offset, LzmaHeader.Size)) { var lzmaHeader = LzmaHeader.Read(stream); using (var compressedStream = GetSubStream(info.Offset + LzmaHeader.Size, lzmaHeader.LzmaSize)) { var uncompressedStream = new MemoryStream(info.UncompressedSize); Decoder decoder = new Decoder(); decoder.SetDecoderProperties(lzmaHeader.Properties); decoder.Code(compressedStream, uncompressedStream, lzmaHeader.LzmaSize, lzmaHeader.ActualSize, null); uncompressedStream.Seek(0, SeekOrigin.Begin); return(uncompressedStream); } } }
/// <summary> /// Decompress byte array compressed with LZMA algorithm (C# inside) /// </summary> /// <param name="data">Byte array to decompress</param> /// <returns>Decompressed byte array</returns> public static byte[] ExtractBytes(byte[] data) { using (var inStream = new MemoryStream(data)) { var decoder = new Decoder(); inStream.Seek(0, 0); using (var outStream = new MemoryStream()) { long outSize; decoder.SetDecoderProperties(GetLzmaProperties(inStream, out outSize)); decoder.Code(inStream, outStream, inStream.Length - inStream.Position, outSize, null); return outStream.ToArray(); } } }
/// <summary> /// Decompress the specified stream (C# inside) /// </summary> /// <param name="inStream">The source compressed stream</param> /// <param name="outStream">The destination uncompressed stream</param> /// <param name="inLength">The length of compressed data (null for inStream.Length)</param> /// <param name="codeProgressEvent">The event for handling the code progress</param> public static void DecompressStream(Stream inStream, Stream outStream, int? inLength, EventHandler<ProgressEventArgs> codeProgressEvent) { if (!inStream.CanRead || !outStream.CanWrite) { throw new ArgumentException("The specified streams are invalid."); } var decoder = new Decoder(); long outSize, inSize = (inLength.HasValue ? inLength.Value : inStream.Length) - inStream.Position; decoder.SetDecoderProperties(GetLzmaProperties(inStream, out outSize)); decoder.Code( inStream, outStream, inSize, outSize, new LzmaProgressCallback(inSize, codeProgressEvent)); }
/// <summary> /// Starts a read-only transaction. /// </summary> /// <remarks> /// This puts the OMod into read-only mode. /// /// Read-only mode can greatly increase the speed at which multiple file are extracted. /// </remarks> public void BeginReadOnlyTransaction(FileUtil p_futFileUtil) { if (!IsPacked) { m_arcFile.ReadOnlyInitProgressUpdated += new CancelProgressEventHandler(ArchiveFile_ReadOnlyInitProgressUpdated); m_arcFile.BeginReadOnlyTransaction(p_futFileUtil); return; } m_strReadOnlyTempDirectory = p_futFileUtil.CreateTempDirectory(); string[] strFileStreamNames = { "plugins", "data" }; List<FileInfo> lstFiles = null; byte[] bteUncompressedFileData = null; m_intReadOnlyInitFileBlockExtractionCurrentStage = 0; m_fltReadOnlyInitCurrentBaseProgress = 0; foreach (string strFileStreamName in strFileStreamNames) { //extract the compressed file block... using (Stream stmCompressedFiles = new MemoryStream()) { using (SevenZipExtractor szeOmod = new SevenZipExtractor(m_strFilePath)) { if (!szeOmod.ArchiveFileNames.Contains(strFileStreamName)) continue; m_intReadOnlyInitFileBlockExtractionCurrentStage++; szeOmod.ExtractFile(strFileStreamName, stmCompressedFiles); switch (strFileStreamName) { case "plugins": lstFiles = PluginList; break; case "data": lstFiles = DataFileList; break; default: throw new Exception("Unexpected value for file stream name: " + strFileStreamName); } } stmCompressedFiles.Position = 0; Int64 intTotalLength = lstFiles.Sum(x => x.Length); bteUncompressedFileData = new byte[intTotalLength]; switch (CompressionType) { case InArchiveFormat.SevenZip: byte[] bteProperties = new byte[5]; stmCompressedFiles.Read(bteProperties, 0, 5); Decoder dcrDecoder = new Decoder(); dcrDecoder.SetDecoderProperties(bteProperties); DecoderProgressWatcher dpwWatcher = new DecoderProgressWatcher(stmCompressedFiles.Length); dpwWatcher.ProgressUpdated += new EventHandler<EventArgs<int>>(dpwWatcher_ProgressUpdated); using (Stream stmUncompressedFiles = new MemoryStream(bteUncompressedFileData)) dcrDecoder.Code(stmCompressedFiles, stmUncompressedFiles, stmCompressedFiles.Length - stmCompressedFiles.Position, intTotalLength, dpwWatcher); break; case InArchiveFormat.Zip: using (SevenZipExtractor szeZip = new SevenZipExtractor(stmCompressedFiles)) { szeZip.Extracting += new EventHandler<ProgressEventArgs>(szeZip_Extracting); using (Stream stmFile = new MemoryStream(bteUncompressedFileData)) { szeZip.ExtractFile(0, stmFile); } } break; default: throw new Exception("Cannot get files: unsupported compression type: " + CompressionType.ToString()); } } float fltFileStreamPercentBlockSize = FILE_BLOCK_EXTRACTION_PROGRESS_BLOCK_SIZE / m_intReadOnlyInitFileBlockExtractionStages; float fltFileWritingPercentBlockSize = FILE_WRITE_PROGRESS_BLOCK_SIZE / m_intReadOnlyInitFileBlockExtractionStages; m_fltReadOnlyInitCurrentBaseProgress += fltFileStreamPercentBlockSize; //...then write each file to the temporary location Int64 intFileStart = 0; byte[] bteFile = null; Crc32 crcChecksum = new Crc32(); for (Int32 i = 0; i < lstFiles.Count; i++) { FileInfo ofiFile = lstFiles[i]; bteFile = new byte[ofiFile.Length]; Array.Copy(bteUncompressedFileData, intFileStart, bteFile, 0, ofiFile.Length); intFileStart += ofiFile.Length; FileUtil.WriteAllBytes(Path.Combine(m_strReadOnlyTempDirectory, ofiFile.Name), bteFile); crcChecksum.Initialize(); crcChecksum.ComputeHash(bteFile); if (crcChecksum.CrcValue != ofiFile.CRC) throw new Exception(String.Format("Unable to extract {0}: checksums did not match. OMod is corrupt.", ofiFile.Name)); UpdateReadOnlyInitProgress(m_fltReadOnlyInitCurrentBaseProgress, fltFileWritingPercentBlockSize, i / lstFiles.Count); } m_fltReadOnlyInitCurrentBaseProgress += fltFileWritingPercentBlockSize; } m_fltReadOnlyInitCurrentBaseProgress = FILE_BLOCK_EXTRACTION_PROGRESS_BLOCK_SIZE + FILE_WRITE_PROGRESS_BLOCK_SIZE; Int32 intRemainingSteps = (m_booHasInstallScript ? 1 : 0) + (m_booHasReadme ? 1 : 0) + (m_booHasScreenshot ? 1 : 0); Int32 intStepCounter = 1; if (m_booHasScreenshot) { File.WriteAllBytes(Path.Combine(m_strReadOnlyTempDirectory, ScreenshotPath), GetSpecialFile(ScreenshotPath)); UpdateReadOnlyInitProgress(m_fltReadOnlyInitCurrentBaseProgress, 1f - m_fltReadOnlyInitCurrentBaseProgress, 1f / intRemainingSteps * intStepCounter++); } if (m_booHasInstallScript) { File.WriteAllBytes(Path.Combine(m_strReadOnlyTempDirectory, "script"), GetSpecialFile("script")); UpdateReadOnlyInitProgress(m_fltReadOnlyInitCurrentBaseProgress, 1f - m_fltReadOnlyInitCurrentBaseProgress, 1f / intRemainingSteps * intStepCounter++); } if (m_booHasReadme) { File.WriteAllBytes(Path.Combine(m_strReadOnlyTempDirectory, "readme"), GetSpecialFile("readme")); UpdateReadOnlyInitProgress(m_fltReadOnlyInitCurrentBaseProgress, 1f - m_fltReadOnlyInitCurrentBaseProgress, 1f / intRemainingSteps * intStepCounter); } }
/// <summary> /// Retrieves the specified file from the OMod. /// </summary> /// <param name="p_strFile">The file to retrieve.</param> /// <returns>The requested file data.</returns> /// <exception cref="FileNotFoundException">Thrown if the specified file /// is not in the OMod.</exception> public byte[] GetFile(string p_strFile) { if (!ContainsFile(p_strFile)) throw new FileNotFoundException("File doesn't exist in OMod", p_strFile); if (!IsPacked) { if ((m_arcCacheFile != null) && m_arcCacheFile.ContainsFile(GetRealPath(p_strFile))) return m_arcCacheFile.GetFileContents(GetRealPath(p_strFile)); return m_arcFile.GetFileContents(GetRealPath(p_strFile)); } if (!String.IsNullOrEmpty(m_strReadOnlyTempDirectory)) return File.ReadAllBytes(Path.Combine(m_strReadOnlyTempDirectory, p_strFile)); List<FileInfo> lstFiles = null; byte[] bteFileBlock = null; using (Stream stmDataFiles = new MemoryStream()) { using (SevenZipExtractor szeOmod = new SevenZipExtractor(m_strFilePath)) { if (Path.GetExtension(p_strFile).Equals(".esm", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(p_strFile).Equals(".esp", StringComparison.OrdinalIgnoreCase)) { szeOmod.ExtractFile("plugins", stmDataFiles); lstFiles = PluginList; } else { szeOmod.ExtractFile("data", stmDataFiles); lstFiles = DataFileList; } } stmDataFiles.Position = 0; Int64 intTotalLength = lstFiles.Sum(x => x.Length); bteFileBlock = new byte[intTotalLength]; switch (CompressionType) { case InArchiveFormat.SevenZip: byte[] bteProperties = new byte[5]; stmDataFiles.Read(bteProperties, 0, 5); Decoder dcrDecoder = new Decoder(); dcrDecoder.SetDecoderProperties(bteProperties); using (Stream stmFile = new MemoryStream(bteFileBlock)) dcrDecoder.Code(stmDataFiles, stmFile, stmDataFiles.Length - stmDataFiles.Position, intTotalLength, null); break; case InArchiveFormat.Zip: using (SevenZipExtractor szeZip = new SevenZipExtractor(stmDataFiles)) { using (Stream stmFile = new MemoryStream(bteFileBlock)) { szeZip.ExtractFile(0, stmFile); } } break; default: throw new Exception("Cannot get file: unsupported compression type: " + CompressionType.ToString()); } } Int64 intFileStart = 0; byte[] bteFile = null; foreach (FileInfo ofiFile in lstFiles) { if (!ofiFile.Name.Equals(p_strFile, StringComparison.OrdinalIgnoreCase)) intFileStart += ofiFile.Length; else { bteFile = new byte[ofiFile.Length]; Array.Copy(bteFileBlock, intFileStart, bteFile, 0, ofiFile.Length); break; } } return bteFile; }
private int ReadLumpValues <T>(LumpType type, int srcOffset, T[] dst, int dstOffset, int count) where T : struct { var info = GetLumpInfo(type); // 0 = no compression if (info.UncompressedSize == 0) { var tSize = Marshal.SizeOf <T>(); var length = info.Length / tSize; if (srcOffset > length) { srcOffset = length; } if (srcOffset + count > length) { count = length - srcOffset; } if (count <= 0) { return(0); } using (var stream = GetLumpStream(type)) { stream.Seek(tSize * srcOffset, SeekOrigin.Begin); LumpReader <T> .ReadLumpFromStream(stream, count, dst, dstOffset); } } else { // LZMA compressed lump if (type == LumpType.GAME_LUMP) { // game lumps are compressed individually // https://developer.valvesoftware.com/wiki/Source_BSP_File_Format#Lump_compression throw new NotImplementedException(); } using (var stream = GetSubStream(info.Offset, LzmaHeader.Size)) { var lzmaHeader = LzmaHeader.Read(stream); using (var compressedStream = GetSubStream(info.Offset + LzmaHeader.Size, lzmaHeader.LzmaSize)) { using (var uncompressedStream = new MemoryStream(info.UncompressedSize)) { Decoder decoder = new Decoder(); decoder.SetDecoderProperties(lzmaHeader.Properties); decoder.Code(compressedStream, uncompressedStream, lzmaHeader.LzmaSize, lzmaHeader.ActualSize, null); var tSize = Marshal.SizeOf <T>(); var length = info.UncompressedSize / tSize; if (srcOffset > length) { srcOffset = length; } if (srcOffset + count > length) { count = length - srcOffset; } if (count <= 0) { return(0); } uncompressedStream.Seek(tSize * srcOffset, SeekOrigin.Begin); LumpReader <T> .ReadLumpFromStream(uncompressedStream, count, dst, dstOffset); if (type == LumpType.PLANES) { return(count); } } } } } return(count); }
/// <summary> /// Provides a filter for decompressing an LZMA encoded <see cref="Stream"/>. /// </summary> /// <param name="stream">The underlying <see cref="Stream"/> providing the compressed data.</param> /// <param name="bufferSize">The maximum number of uncompressed bytes to buffer. 32k (the step size of <see cref="SevenZip"/>) is a sensible minimum.</param> /// <exception cref="IOException">The <paramref name="stream"/> doesn't start with a valid 5-bit LZMA header.</exception> /// <remarks> /// This method internally uses multi-threading and a <see cref="CircularBufferStream"/>. /// The <paramref name="stream"/> may be closed with a delay. /// </remarks> private static Stream GetDecompressionStream(Stream stream, int bufferSize = 128 * 1024) { var bufferStream = new CircularBufferStream(bufferSize); var decoder = new Decoder(); // Read LZMA header if (stream.CanSeek) stream.Position = 0; try { decoder.SetDecoderProperties(stream.Read(5)); } #region Error handling catch (IOException ex) { // Wrap exception to add context throw new IOException(Resources.ArchiveInvalid, ex); } catch (ApplicationException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(Resources.ArchiveInvalid, ex); } #endregion // Detmerine uncompressed length long uncompressedLength = 0; if (BitConverter.IsLittleEndian) { for (int i = 0; i < 8; i++) { int v = stream.ReadByte(); if (v < 0) throw new IOException(Resources.ArchiveInvalid); uncompressedLength |= ((long)(byte)v) << (8 * i); } } // If the uncompressed length is unknown, use original size * 1.5 as an estimate unchecked { bufferStream.SetLength(uncompressedLength == -1 ? stream.Length : (long)(uncompressedLength * 1.5)); } // Initialize the producer thread that will deliver uncompressed data var thread = new Thread(() => { try { decoder.Code(stream, bufferStream, stream.Length, uncompressedLength, null); } #region Error handling catch (ThreadAbortException) {} catch (ObjectDisposedException) { // If the buffer stream is closed too early the user probably just canceled the extraction process } catch (ApplicationException ex) { // Wrap exception since only certain exception types are allowed bufferStream.RelayErrorToReader(new IOException(ex.Message, ex)); } #endregion finally { bufferStream.DoneWriting(); } }) {IsBackground = true}; thread.Start(); return new DisposeWarpperStream(bufferStream, () => { // Stop producer thread when the buffer stream is closed thread.Abort(); thread.Join(); stream.Dispose(); }); }
/// <summary> /// Provides a filter for decompressing an LZMA encoded <see cref="Stream"/>. /// </summary> /// <param name="stream">The underlying <see cref="Stream"/> providing the compressed data.</param> /// <param name="bufferSize">The maximum number of uncompressed bytes to buffer. 32k (the step size of <see cref="SevenZip"/>) is a sensible minimum.</param> /// <exception cref="IOException">The <paramref name="stream"/> doesn't start with a valid 5-bit LZMA header.</exception> /// <remarks> /// This method internally uses multi-threading and a <see cref="CircularBufferStream"/>. /// The <paramref name="stream"/> may be closed with a delay. /// </remarks> internal static Stream GetDecompressionStream(Stream stream, int bufferSize = 128 * 1024) { var bufferStream = new CircularBufferStream(bufferSize); var decoder = new Decoder(); // Read LZMA header if (stream.CanSeek) stream.Position = 0; try { decoder.SetDecoderProperties(stream.Read(5)); } #region Error handling catch (IOException ex) { // Wrap exception to add context throw new IOException(Resources.ArchiveInvalid, ex); } catch (ApplicationException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(Resources.ArchiveInvalid, ex); } #endregion // Read "uncompressed length" header var uncompressedLengthData = stream.Read(8); if (!BitConverter.IsLittleEndian) Array.Reverse(uncompressedLengthData); long uncompressedLength = BitConverter.ToInt64(uncompressedLengthData, startIndex: 0); bufferStream.SetLength((uncompressedLength == UnknownSize) ? (long)(stream.Length * 1.5) : uncompressedLength); var producerThread = new Thread(() => { try { decoder.Code( inStream: stream, outStream: bufferStream, inSize: UnknownSize, outSize: uncompressedLength, progress: null); } catch (ThreadAbortException) {} catch (ObjectDisposedException) { // If the buffer stream is closed too early the user probably just canceled the extraction process } catch (ApplicationException ex) { bufferStream.RelayErrorToReader(new IOException(ex.Message, ex)); } finally { bufferStream.DoneWriting(); } }) {IsBackground = true}; producerThread.Start(); return new DisposeWarpperStream(bufferStream, disposeHandler: () => { producerThread.Abort(); producerThread.Join(); stream.Dispose(); }); }