diff --git a/SabreTools.Helper/Data/Enums.cs b/SabreTools.Helper/Data/Enums.cs index 30ff725b..d2ac89b6 100644 --- a/SabreTools.Helper/Data/Enums.cs +++ b/SabreTools.Helper/Data/Enums.cs @@ -80,6 +80,88 @@ TorrentLrzip = 7, } + /// + /// RAR extra area flag + /// + public enum RarExtraAreaFlag : uint + { + FileEncryption = 0x01, + FileHash = 0x02, + FileTime = 0x03, + FileVersion = 0x04, + Redirection = 0x05, + UnixOwner = 0x06, + ServiceData = 0x07, + } + + /// + /// RAR header types + /// + public enum RarHeaderType : uint + { + MainArchiveHeader = 1, + File = 2, + Service = 3, + ArchiveEncryption = 4, + EndOfArchive = 5, + } + + /// + /// RAR entry redirection type + /// + public enum RarRedirectionType : uint + { + UnixSymlink = 0x0001, + WindowsSymlink = 0x0002, + WindowsJunction = 0x0003, + HardLink = 0x0004, + FileCopy = 0x0005, + } + + /// + /// 7zip Properties + /// + public enum SevenZipProperties : uint + { + kEnd = 0x00, + + kHeader = 0x01, + + kArchiveProperties = 0x02, + + kAdditionalStreamsInfo = 0x03, + kMainStreamsInfo = 0x04, + kFilesInfo = 0x05, + + kPackInfo = 0x06, + kUnPackInfo = 0x07, + kSubStreamsInfo = 0x08, + + kSize = 0x09, + kCRC = 0x0A, + + kFolder = 0x0B, + + kCodersUnPackSize = 0x0C, + kNumUnPackStream = 0x0D, + + kEmptyStream = 0x0E, + kEmptyFile = 0x0F, + kAnti = 0x10, + + kName = 0x11, + kCTime = 0x12, + kATime = 0x13, + kMTime = 0x14, + kWinAttributes = 0x15, + kComment = 0x16, + + kEncodedHeader = 0x17, + + kStartPos = 0x18, + kDummy = 0x19, + } + /// /// Zip open type /// diff --git a/SabreTools.Helper/Data/Flags.cs b/SabreTools.Helper/Data/Flags.cs index 746ace74..d9f1f2c8 100644 --- a/SabreTools.Helper/Data/Flags.cs +++ b/SabreTools.Helper/Data/Flags.cs @@ -86,6 +86,90 @@ namespace SabreTools.Helper.Data Bit2 = 0x0004, } + /// + /// RAR archive flags + /// + [Flags] + public enum RarArchiveFlags : uint + { + Volume = 0x0001, // Volume. Archive is a part of multivolume set. + VolumeNumberField = 0x0002, // Volume number field is present. This flag is present in all volumes except first. + Solid = 0x0004, // Solid archive. + RecoveryRecordPresent = 0x0008, // Recovery record is present. + Locked = 0x0010, // Locked archive. + } + + /// + /// RAR entry encryption flags + /// + [Flags] + public enum RarEncryptionFlags : uint + { + PasswordCheckDataPresent = 0x0001, + UseTweakedChecksums = 0x0002, + + /* + If flag 0x0002 is present, RAR transforms the checksum preserving file or service data integrity, so it becomes dependent on + encryption key. It makes guessing file contents based on checksum impossible. It affects both data CRC32 in file header and + checksums in file hash record in extra area. + */ + } + + /// + /// RAR file flags + /// + [Flags] + public enum RarFileFlags : uint + { + Directory = 0x0001, // Directory file system object (file header only) + TimeInUnix = 0x0002, // Time field in Unix format is present + CRCPresent = 0x0004, // CRC32 field is present + UnpackedSizeUnknown = 0x0008, // Unpacked size is unknown + + /* + If flag 0x0008 is set, unpacked size field is still present, but must be ignored and extraction + must be performed until reaching the end of compression stream. This flag can be set if actual + file size is larger than reported by OS or if file size is unknown such as for all volumes except + last when archiving from stdin to multivolume archive + */ + } + + /// + /// RAR header flags + /// + [Flags] + public enum RarHeaderFlags : uint + { + ExtraAreaPresent = 0x0001, // Extra area is present in the end of header + DataAreaPresent = 0x0002, // Data area is present in the end of header + BlocksWithUnknownType = 0x0004, // Blocks with unknown type and this flag must be skipped when updating an archive + DataAreaContinuingFromPrevious = 0x0008, // Data area is continuing from previous volume + DataAreaContinuingToNext = 0x0010, // Data area is continuing in next volume + BlockDependsOnPreceding = 0x0020, // Block depends on preceding file block + PreserveChildBlock = 0x0040, // Preserve a child block if host block is modified + } + + [Flags] + public enum RarUnixOwnerRecordFlags : uint + { + UserNameStringIsPresent = 0x0001, + GroupNameStringIsPresent = 0x0002, + NumericUserIdIsPresent = 0x0004, + NumericGroupIdIsPresent = 0x0008, + } + + /// + /// RAR entry time flags + /// + [Flags] + public enum RarTimeFlags : uint + { + TimeInUnixFormat = 0x0001, + ModificationTimePresent = 0x0002, + CreationTimePresent = 0x0004, + LastAccessTimePresent = 0x0008, + } + /// /// Zipfile special status /// diff --git a/SabreTools.Helper/External/CoreRarArchive.cs b/SabreTools.Helper/External/CoreRarArchive.cs new file mode 100644 index 00000000..287dc204 --- /dev/null +++ b/SabreTools.Helper/External/CoreRarArchive.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; + +using SabreTools.Helper.Data; + +/// +/// http://www.rarlab.com/technote.htm#srvheaders +/// +namespace SabreTools.Helper.Tools +{ + public class CoreRarArchive + { + // SFX Module Information + public byte[] SFX; + + // Standard Header Information + public uint HeaderCRC32; + public uint HeaderSize; // vint + public RarHeaderFlags HeaderFlags; // vint + public uint ExtraAreaSize; // vint + public RarArchiveFlags ArchiveFlags; // vint + public uint VolumeNumber; // vint + public byte[] ExtraArea; + + // Encryption Header Information + public uint EncryptionHeaderCRC32; + public uint EncryptionHeaderSize; // vint + public RarHeaderFlags EncryptionHeaderFlags; // vint + public uint EncryptionVersion; // vint + public uint EncryptionFlags; // vint + public byte KDFCount; + public byte[] Salt = new byte[16]; + public byte[] CheckValue = new byte[12]; + + // Locator Information + public uint LocatorSize; // vint + public uint LocatorFlags; // vint + public uint QuickOpenOffset; // vint + public uint RecoveryRecordOffset; // vint + + // Entry Information + public List Entries = new List(); + } + + public class CoreRarArchiveEntry + { + // Standard Entry Information + public uint HeaderCRC32; + public uint HeaderSize; // vint + public RarHeaderType HeaderType; // vint + public RarHeaderFlags HeaderFlags; // vint + public uint ExtraAreaSize; // vint + public uint DataAreaSize; // vint + public RarFileFlags FileFlags; // vint + public uint UnpackedSize; // vint + public uint Attributes; // vint + public uint mtime; + public uint DataCRC32; + public uint CompressionInformation; // vint + public uint HostOS; // vint + public uint NameLength; // vint + public byte[] Name; + public byte[] DataArea; + + // File Encryption Information + public uint EncryptionSize; // vint + public RarEncryptionFlags EncryptionFlags; // vint + public byte KDFCount; + public byte[] Salt = new byte[16]; + public byte[] IV = new byte[16]; + public byte[] CheckValue = new byte[12]; + + // File Hash Information + public uint HashSize; // vint + public uint HashType; // vint + public byte[] HashData = new byte[32]; + + // File Time Information + public uint TimeSize; // vint + public RarTimeFlags TimeFlags; // vint + public uint TimeMtime; + public ulong TimeMtime64; + public uint TimeCtime; + public ulong TimeCtime64; + public uint TimeLtime; + public ulong TimeLtime64; + + // File Version Information + public uint VersionSize; // vint + public const uint VersionFlags = 0; // vint + public uint VersionNumber; // vint + + // File System Redirection Record + public uint RedirectionSize; // vint + public RarRedirectionType RedirectionType; // vint + public uint RedirectionFlags; // vint + public uint RedirectionNameLength; // vint + public byte[] RedirectionName; + + // Unix Owner Record + public uint UnixOwnerSize; // vint + public RarUnixOwnerRecordFlags UnixOwnerFlags; // vint + public uint UnixOwnerUserNameLength; // vint + public byte[] UnixOwnerUserName; + public uint UnixOwnerGroupNameLength; // vint + public byte[] UnixOwnerGroupName; + public uint UnixOwnerUserId; // vint + public uint UnixOwnerGroupId; // vint + + // Service Data Information + public uint ServiceSize; // vint + public byte[] ServiceData; + } +} diff --git a/SabreTools.Helper/SabreTools.Helper.csproj b/SabreTools.Helper/SabreTools.Helper.csproj index efd138c4..c0d56eb3 100644 --- a/SabreTools.Helper/SabreTools.Helper.csproj +++ b/SabreTools.Helper/SabreTools.Helper.csproj @@ -118,6 +118,7 @@ + diff --git a/SabreTools.Helper/Tools/ArchiveTools.cs b/SabreTools.Helper/Tools/ArchiveTools.cs index 70c4b3bb..0b26e7b7 100644 --- a/SabreTools.Helper/Tools/ArchiveTools.cs +++ b/SabreTools.Helper/Tools/ArchiveTools.cs @@ -843,19 +843,220 @@ namespace SabreTools.Helper.Tools } /// - /// (UNIMPLEMENTED) Read the information from an input 7z file + /// (INCOMPLETE) Retrieve file information for a RAR file /// - /// Name of the input file to check + /// Filename to get information from /// Logger object for file and console output - /// - /// http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt - public static void GetSevenZipFileInfo(string input, Logger logger) + public static void GetRarFileInfo(string input, Logger logger) { + if (!File.Exists(input)) + { + return; + } + BinaryReader br = new BinaryReader(File.OpenRead(input)); - br.ReadBytes(6); // BYTE kSignature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; - logger.User("ArchiveVersion (Major.Minor): " + br.ReadByte() + "." + br.ReadByte()); - logger.User("StartHeaderCRC: " + br.ReadUInt32()); - logger.User("StartHeader (NextHeaderOffset, NextHeaderSize, NextHeaderCRC)" + br.ReadUInt64() + ", " + br.ReadUInt64() + ", " + br.ReadUInt32()); + + // Check for the signature first (Skipping the SFX Module) + byte[] signature = br.ReadBytes(8); + int startpos = 0; + while (startpos < Constants.MibiByte && BitConverter.ToString(signature, 0, 7) != Constants.RarSig && BitConverter.ToString(signature) != Constants.RarFiveSig) + { + startpos++; + br.BaseStream.Position = startpos; + signature = br.ReadBytes(8); + } + if (BitConverter.ToString(signature, 0, 7) != Constants.RarSig && BitConverter.ToString(signature) != Constants.RarFiveSig) + { + return; + } + + CoreRarArchive cra = new CoreRarArchive(); + if (startpos > 0) + { + br.BaseStream.Position = 0; + cra.SFX = br.ReadBytes(startpos); + } + + // Get all archive header information + cra.HeaderCRC32 = br.ReadUInt32(); + cra.HeaderSize = br.ReadUInt32(); + uint headerType = br.ReadUInt32(); + + // Special encryption information + bool hasEncryptionHeader = false; + + // If it's encrypted + if (headerType == (uint)RarHeaderType.ArchiveEncryption) + { + hasEncryptionHeader = true; + cra.EncryptionHeaderCRC32 = cra.HeaderCRC32; + cra.EncryptionHeaderSize = cra.HeaderSize; + cra.EncryptionHeaderFlags = (RarHeaderFlags)br.ReadUInt32(); + cra.EncryptionVersion = br.ReadUInt32(); + cra.EncryptionFlags = br.ReadUInt32(); + cra.KDFCount = br.ReadByte(); + cra.Salt = br.ReadBytes(16); + cra.CheckValue = br.ReadBytes(12); + + cra.HeaderCRC32 = br.ReadUInt32(); + cra.HeaderSize = br.ReadUInt32(); + headerType = br.ReadUInt32(); + } + + cra.HeaderFlags = (RarHeaderFlags)br.ReadUInt32(); + if ((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) + { + cra.ExtraAreaSize = br.ReadUInt32(); + } + cra.ArchiveFlags = (RarArchiveFlags)br.ReadUInt32(); + if ((cra.ArchiveFlags & RarArchiveFlags.VolumeNumberField) != 0) + { + cra.VolumeNumber = br.ReadUInt32(); + } + if (((cra.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) && cra.ExtraAreaSize != 0) + { + cra.ExtraArea = br.ReadBytes((int)cra.ExtraAreaSize); + } + + // Archive Comment Service Header + + // Now for file headers + for (;;) + { + CoreRarArchiveEntry crae = new CoreRarArchiveEntry(); + crae.HeaderCRC32 = br.ReadUInt32(); + crae.HeaderSize = br.ReadUInt32(); + crae.HeaderType = (RarHeaderType)br.ReadUInt32(); + + if (crae.HeaderType == RarHeaderType.EndOfArchive) + { + break; + } + + crae.HeaderFlags = (RarHeaderFlags)br.ReadUInt32(); + if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) + { + crae.ExtraAreaSize = br.ReadUInt32(); + } + if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0) + { + crae.DataAreaSize = br.ReadUInt32(); + } + crae.FileFlags = (RarFileFlags)br.ReadUInt32(); + crae.UnpackedSize = br.ReadUInt32(); + if ((crae.FileFlags & RarFileFlags.UnpackedSizeUnknown) != 0) + { + crae.UnpackedSize = 0; + } + crae.Attributes = br.ReadUInt32(); + crae.mtime = br.ReadUInt32(); + crae.DataCRC32 = br.ReadUInt32(); + crae.CompressionInformation = br.ReadUInt32(); + crae.HostOS = br.ReadUInt32(); + crae.NameLength = br.ReadUInt32(); + crae.Name = br.ReadBytes((int)crae.NameLength); + if ((crae.HeaderFlags & RarHeaderFlags.ExtraAreaPresent) != 0) + { + uint extraSize = br.ReadUInt32(); + switch (br.ReadUInt32()) // Extra Area Type + { + case 0x01: // File encryption information + crae.EncryptionSize = extraSize; + crae.EncryptionFlags = (RarEncryptionFlags)br.ReadUInt32(); + crae.KDFCount = br.ReadByte(); + crae.Salt = br.ReadBytes(16); + crae.IV = br.ReadBytes(16); + crae.CheckValue = br.ReadBytes(12); + break; + + case 0x02: // File data hash + crae.HashSize = extraSize; + crae.HashType = br.ReadUInt32(); + crae.HashData = br.ReadBytes(32); + break; + + case 0x03: // High precision file time + crae.TimeSize = extraSize; + crae.TimeFlags = (RarTimeFlags)br.ReadUInt32(); + if ((crae.TimeFlags & RarTimeFlags.TimeInUnixFormat) != 0) + { + if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0) + { + crae.TimeMtime64 = br.ReadUInt64(); + } + if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0) + { + crae.TimeCtime64 = br.ReadUInt64(); + } + if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0) + { + crae.TimeLtime64 = br.ReadUInt64(); + } + } + else + { + if ((crae.TimeFlags & RarTimeFlags.ModificationTimePresent) != 0) + { + crae.TimeMtime = br.ReadUInt32(); + } + if ((crae.TimeFlags & RarTimeFlags.CreationTimePresent) != 0) + { + crae.TimeCtime = br.ReadUInt32(); + } + if ((crae.TimeFlags & RarTimeFlags.LastAccessTimePresent) != 0) + { + crae.TimeLtime = br.ReadUInt32(); + } + } + break; + + case 0x04: // File version number + crae.VersionSize = extraSize; + /* crae.VersionFlags = */ br.ReadUInt32(); + crae.VersionNumber = br.ReadUInt32(); + break; + + case 0x05: // File system redirection + crae.RedirectionSize = extraSize; + crae.RedirectionType = (RarRedirectionType)br.ReadUInt32(); + crae.RedirectionFlags = br.ReadUInt32(); + crae.RedirectionNameLength = br.ReadUInt32(); + crae.RedirectionName = br.ReadBytes((int)crae.RedirectionNameLength); + break; + + case 0x06: // Unix owner and group information + crae.UnixOwnerSize = extraSize; + crae.UnixOwnerFlags = (RarUnixOwnerRecordFlags)br.ReadUInt32(); + if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.UserNameStringIsPresent) != 0) + { + crae.UnixOwnerUserNameLength = br.ReadUInt32(); + crae.UnixOwnerUserName = br.ReadBytes((int)crae.UnixOwnerUserNameLength); + } + if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.GroupNameStringIsPresent) != 0) + { + crae.UnixOwnerGroupNameLength = br.ReadUInt32(); + crae.UnixOwnerGroupName = br.ReadBytes((int)crae.UnixOwnerGroupNameLength); + } + if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericUserIdIsPresent) != 0) + { + crae.UnixOwnerUserId = br.ReadUInt32(); + } + if ((crae.UnixOwnerFlags & RarUnixOwnerRecordFlags.NumericGroupIdIsPresent) != 0) + { + crae.UnixOwnerGroupId = br.ReadUInt32(); + } + break; + + case 0x07: // Service header data array + + break; + } + } + if ((crae.HeaderFlags & RarHeaderFlags.DataAreaPresent) != 0) + { + crae.DataArea = br.ReadBytes((int)crae.DataAreaSize); + } + } } #endregion diff --git a/SabreTools.Helper/Tools/FileTools.cs b/SabreTools.Helper/Tools/FileTools.cs index 34c5951c..77c1944b 100644 --- a/SabreTools.Helper/Tools/FileTools.cs +++ b/SabreTools.Helper/Tools/FileTools.cs @@ -1,9 +1,7 @@ -using Mono.Data.Sqlite; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; -using System.Threading.Tasks; using System.Xml; using System.Xml.Schema;