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;