/* This file is part of SevenZipSharp. SevenZipSharp is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SevenZipSharp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with SevenZipSharp. If not, see . */ #define DOTNET20 #define UNMANAGED #define COMPRESS using System; using System.IO; namespace SevenZip { #if UNMANAGED /// /// The signature checker class. Original code by Siddharth Uppal, adapted by Markhor. /// /// Based on the code at http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/# internal static class FileChecker { private const int SIGNATURE_SIZE = 16; private const int SFX_SCAN_LENGTH = 256 * 1024; private static bool SpecialDetect(Stream stream, int offset, InArchiveFormat expectedFormat) { if (stream.Length > offset + SIGNATURE_SIZE) { var signature = new byte[SIGNATURE_SIZE]; int bytesRequired = SIGNATURE_SIZE; int index = 0; stream.Seek(offset, SeekOrigin.Begin); while (bytesRequired > 0) { int bytesRead = stream.Read(signature, index, bytesRequired); bytesRequired -= bytesRead; index += bytesRead; } string actualSignature = BitConverter.ToString(signature); foreach (string expectedSignature in Formats.InSignatureFormats.Keys) { if (Formats.InSignatureFormats[expectedSignature] != expectedFormat) { continue; } if (actualSignature.StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } /// /// Gets the InArchiveFormat for a specific extension. /// /// The stream to identify. /// The archive beginning offset. /// True if the original format of the stream is PE; otherwise, false. /// Corresponding InArchiveFormat. public static InArchiveFormat CheckSignature(Stream stream, out int offset, out bool isExecutable) { offset = 0; if (!stream.CanRead) { throw new ArgumentException("The stream must be readable."); } if (stream.Length < SIGNATURE_SIZE) { throw new ArgumentException("The stream is invalid."); } #region Get file signature var signature = new byte[SIGNATURE_SIZE]; int bytesRequired = SIGNATURE_SIZE; int index = 0; stream.Seek(0, SeekOrigin.Begin); while (bytesRequired > 0) { int bytesRead = stream.Read(signature, index, bytesRequired); bytesRequired -= bytesRead; index += bytesRead; } string actualSignature = BitConverter.ToString(signature); #endregion InArchiveFormat suspectedFormat = InArchiveFormat.XZ; // any except PE and Cab isExecutable = false; foreach (string expectedSignature in Formats.InSignatureFormats.Keys) { if (actualSignature.StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase) || actualSignature.Substring(6).StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase) && Formats.InSignatureFormats[expectedSignature] == InArchiveFormat.Lzh) { if (Formats.InSignatureFormats[expectedSignature] == InArchiveFormat.PE) { suspectedFormat = InArchiveFormat.PE; isExecutable = true; } else { return Formats.InSignatureFormats[expectedSignature]; } } } // Many Microsoft formats if (actualSignature.StartsWith("D0-CF-11-E0-A1-B1-1A-E1", StringComparison.OrdinalIgnoreCase)) { suspectedFormat = InArchiveFormat.Cab; // != InArchiveFormat.XZ } #region SpecialDetect try { SpecialDetect(stream, 257, InArchiveFormat.Tar); } catch (ArgumentException) {} if (SpecialDetect(stream, 0x8001, InArchiveFormat.Iso)) { return InArchiveFormat.Iso; } if (SpecialDetect(stream, 0x8801, InArchiveFormat.Iso)) { return InArchiveFormat.Iso; } if (SpecialDetect(stream, 0x9001, InArchiveFormat.Iso)) { return InArchiveFormat.Iso; } if (SpecialDetect(stream, 0x9001, InArchiveFormat.Iso)) { return InArchiveFormat.Iso; } if (SpecialDetect(stream, 0x400, InArchiveFormat.Hfs)) { return InArchiveFormat.Hfs; } #region Last resort for tar - can mistake if (stream.Length >= 1024) { stream.Seek(-1024, SeekOrigin.End); byte[] buf = new byte[1024]; stream.Read(buf, 0, 1024); bool istar = true; for (int i = 0; i < 1024; i++) { istar = istar && buf[i] == 0; } if (istar) { return InArchiveFormat.Tar; } } #endregion #endregion #region Check if it is an SFX archive or a file with an embedded archive. if (suspectedFormat != InArchiveFormat.XZ) { #region Get first Min(stream.Length, SFX_SCAN_LENGTH) bytes var scanLength = Math.Min(stream.Length, SFX_SCAN_LENGTH); signature = new byte[scanLength]; bytesRequired = (int)scanLength; index = 0; stream.Seek(0, SeekOrigin.Begin); while (bytesRequired > 0) { int bytesRead = stream.Read(signature, index, bytesRequired); bytesRequired -= bytesRead; index += bytesRead; } actualSignature = BitConverter.ToString(signature); #endregion foreach (var format in new InArchiveFormat[] { InArchiveFormat.Zip, InArchiveFormat.SevenZip, InArchiveFormat.Rar, InArchiveFormat.Cab, InArchiveFormat.Arj }) { int pos = actualSignature.IndexOf(Formats.InSignatureFormatsReversed[format]); if (pos > -1) { offset = pos / 3; return format; } } // Nothing if (suspectedFormat == InArchiveFormat.PE) { return InArchiveFormat.PE; } } #endregion throw new ArgumentException("The stream is invalid or no corresponding signature was found."); } /// /// Gets the InArchiveFormat for a specific file name. /// /// The archive file name. /// The archive beginning offset. /// True if the original format of the file is PE; otherwise, false. /// Corresponding InArchiveFormat. /// public static InArchiveFormat CheckSignature(string fileName, out int offset, out bool isExecutable) { using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { try { return CheckSignature(fs, out offset, out isExecutable); } catch (ArgumentException) { offset = 0; isExecutable = false; return Formats.FormatByFileName(fileName, true); } } } } #endif }