예제 #1
0
        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;
        }
예제 #2
0
        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));
 }
예제 #5
0
		/// <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);
			}
		}
예제 #6
0
		/// <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;
		}
예제 #7
0
        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);
        }
예제 #8
0
        /// <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();
            });
        }
예제 #9
0
        /// <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();
            });
        }