mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-23 15:34:34 +00:00
rar5: support ArchiveHeader, additional rar5 header implementation
This commit is contained in:
@@ -33,11 +33,11 @@ namespace SharpCompress.Archives.Rar
|
||||
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
|
||||
yield return part;
|
||||
|
||||
if (!part.ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME))
|
||||
ArchiveHeader ah = part.ArchiveHeader;
|
||||
if (!ah.IsVolume)
|
||||
{
|
||||
yield break; //if file isn't volume then there is no reason to look
|
||||
}
|
||||
ArchiveHeader ah = part.ArchiveHeader;
|
||||
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart);
|
||||
//we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume
|
||||
while (fileInfo != null && fileInfo.Exists)
|
||||
@@ -55,7 +55,7 @@ namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bool oldNumbering = !ah.ArchiveHeaderFlags.HasFlag(ArchiveFlags.NEWNUMBERING)
|
||||
bool oldNumbering = ah.OldNumberingFormat
|
||||
|| currentFilePart.MarkHeader.OldNumberingFormat;
|
||||
if (oldNumbering)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers
|
||||
@@ -7,24 +8,91 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
internal const short mainHeaderSizeWithEnc = 7;
|
||||
internal const short mainHeaderSize = 6;
|
||||
|
||||
public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader)
|
||||
: base(header, reader) {
|
||||
}
|
||||
|
||||
protected override void ReadFromReader(MarkingBinaryReader reader)
|
||||
{
|
||||
HighPosAv = reader.ReadInt16();
|
||||
PosAv = reader.ReadInt32();
|
||||
if (ArchiveHeaderFlags.HasFlag(ArchiveFlags.ENCRYPTVER))
|
||||
if (this.isRar5)
|
||||
{
|
||||
EncryptionVersion = reader.ReadByte();
|
||||
ArchiveHeaderFlags = reader.ReadRarVIntUInt16();
|
||||
if ((ArchiveHeaderFlags & ArchiveFlagsV5.HasVolumeNumber) == ArchiveFlagsV5.HasVolumeNumber)
|
||||
{
|
||||
VolumeNumber = (int)reader.ReadRarVIntUInt32();
|
||||
}
|
||||
if (ExtraSize != 0) {
|
||||
ReadLocator(reader);
|
||||
}
|
||||
} else {
|
||||
ArchiveHeaderFlags = Flags;
|
||||
HighPosAv = reader.ReadInt16();
|
||||
PosAv = reader.ReadInt32();
|
||||
if ((ArchiveHeaderFlags & ArchiveFlagsV4.EncryptVer) == ArchiveFlagsV4.EncryptVer)
|
||||
{
|
||||
EncryptionVersion = reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ArchiveFlags ArchiveHeaderFlags => (ArchiveFlags)Flags;
|
||||
private void ReadLocator(MarkingBinaryReader reader) {
|
||||
var size = reader.ReadRarVIntUInt16();
|
||||
var type = reader.ReadRarVIntUInt16();
|
||||
if (type != 1) throw new InvalidFormatException("expected locator record");
|
||||
var flags = reader.ReadRarVIntUInt16();
|
||||
const ushort HasQuickOpenOffset = 0x01;
|
||||
const ushort HasRecoveryOffset = 0x02;
|
||||
ulong quickOpenOffset = 0;
|
||||
if ((flags & HasQuickOpenOffset) == HasQuickOpenOffset) {
|
||||
quickOpenOffset = reader.ReadRarVInt();
|
||||
}
|
||||
ulong recoveryOffset = 0;
|
||||
if ((flags & HasRecoveryOffset) == HasRecoveryOffset) {
|
||||
recoveryOffset = reader.ReadRarVInt();
|
||||
}
|
||||
}
|
||||
|
||||
internal ushort ArchiveHeaderFlags { get; private set; }
|
||||
|
||||
internal int VolumeNumber { get; private set; }
|
||||
|
||||
internal short HighPosAv { get; private set; }
|
||||
|
||||
internal int PosAv { get; private set; }
|
||||
|
||||
internal byte EncryptionVersion { get; private set; }
|
||||
private byte EncryptionVersion { get; set; }
|
||||
|
||||
public bool HasPassword => ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD);
|
||||
public bool HasPassword => (ArchiveHeaderFlags & ArchiveFlagsV4.Password) == ArchiveFlagsV4.Password;
|
||||
|
||||
public bool OldNumberingFormat {
|
||||
get {
|
||||
return !this.isRar5 &&
|
||||
((ArchiveHeaderFlags & ArchiveFlagsV4.NewNumbering) != ArchiveFlagsV4.NewNumbering);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsVolume {
|
||||
get {
|
||||
return this.isRar5
|
||||
? ((ArchiveHeaderFlags & ArchiveFlagsV5.Volume) == ArchiveFlagsV5.Volume)
|
||||
: ((ArchiveHeaderFlags & ArchiveFlagsV4.Volume) == ArchiveFlagsV4.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFirstVolume {
|
||||
get {
|
||||
return this.isRar5
|
||||
? VolumeNumber == 1
|
||||
: ((ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSolid {
|
||||
get {
|
||||
return this.isRar5
|
||||
? ((ArchiveHeaderFlags & ArchiveFlagsV5.Solid) == ArchiveFlagsV5.Solid)
|
||||
: ((ArchiveHeaderFlags & ArchiveFlagsV4.Solid) == ArchiveFlagsV4.Solid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
highUncompressedkSize = int.MaxValue;
|
||||
}
|
||||
}
|
||||
CompressedSize = UInt32To64(highCompressedSize, AdditionalSize);
|
||||
CompressedSize = UInt32To64(highCompressedSize, AdditionalDataSize);
|
||||
UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize);
|
||||
|
||||
nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize;
|
||||
|
||||
@@ -2,8 +2,14 @@ using System;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
internal enum HeaderType
|
||||
internal enum HeaderType : byte
|
||||
{
|
||||
Rar5ArchiveHeader = 0x01,
|
||||
Rar5FileHeader = 0x02,
|
||||
Rar5ServiceHeader = 0x03,
|
||||
Rar5EncryptionHeader = 0x04,
|
||||
Rar5EndArchiveHeader = 0x05,
|
||||
|
||||
MarkHeader = 0x72,
|
||||
ArchiveHeader = 0x73,
|
||||
FileHeader = 0x74,
|
||||
@@ -16,24 +22,38 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
EndArchiveHeader = 0x7b
|
||||
}
|
||||
|
||||
internal enum HeaderFlags : short
|
||||
internal static class HeaderFlagsV4
|
||||
{
|
||||
LONG_BLOCK = -0x8000
|
||||
public const ushort HasData = 0x8000;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum ArchiveFlags
|
||||
internal static class HeaderFlagsV5
|
||||
{
|
||||
VOLUME = 0x0001,
|
||||
COMMENT = 0x0002,
|
||||
LOCK = 0x0004,
|
||||
SOLID = 0x0008,
|
||||
NEWNUMBERING = 0x0010,
|
||||
AV = 0x0020,
|
||||
PROTECT = 0x0040,
|
||||
PASSWORD = 0x0080,
|
||||
FIRSTVOLUME = 0x0100,
|
||||
ENCRYPTVER = 0x0200
|
||||
public const ushort HasExtra = 0x01;
|
||||
public const ushort HasData = 0x02;
|
||||
}
|
||||
|
||||
internal static class ArchiveFlagsV4
|
||||
{
|
||||
public const ushort Volume = 0x0001;
|
||||
public const ushort Comment = 0x0002;
|
||||
public const ushort Lock = 0x0004;
|
||||
public const ushort Solid = 0x0008;
|
||||
public const ushort NewNumbering = 0x0010;
|
||||
public const ushort AV = 0x0020;
|
||||
public const ushort Protect = 0x0040;
|
||||
public const ushort Password = 0x0080;
|
||||
public const ushort FirstVolume = 0x0100;
|
||||
public const ushort EncryptVer = 0x0200;
|
||||
}
|
||||
|
||||
internal static class ArchiveFlagsV5
|
||||
{
|
||||
public const ushort Volume = 0x0001;
|
||||
public const ushort HasVolumeNumber = 0x0002;
|
||||
public const ushort Solid = 0x0004;
|
||||
public const ushort Protect = 0x0008;
|
||||
public const ushort Lock = 0x0010;
|
||||
}
|
||||
|
||||
internal enum HostOS
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136
|
||||
|
||||
internal bool OldNumberingFormat { get; private set; }
|
||||
|
||||
public bool IsRar5 { get; private set; }
|
||||
|
||||
private MarkHeader(bool isRar5) {
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
Mark = reader.ReadBytes(8);
|
||||
}
|
||||
|
||||
internal uint DataSize => AdditionalSize;
|
||||
internal uint DataSize => AdditionalDataSize;
|
||||
internal byte Version { get; private set; }
|
||||
internal ushort RecSectors { get; private set; }
|
||||
internal uint TotalBlocks { get; private set; }
|
||||
|
||||
@@ -6,8 +6,32 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
internal class RarHeader : IRarHeader
|
||||
{
|
||||
internal const short BaseBlockSize = 7;
|
||||
internal const short LONG_BLOCK = -0x8000;
|
||||
protected readonly bool isRar5;
|
||||
|
||||
protected RarHeader() {
|
||||
//TODO
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private RarHeader(bool isRar5)
|
||||
{
|
||||
this.isRar5 = isRar5;
|
||||
}
|
||||
|
||||
protected RarHeader(RarHeader header, RarCrcBinaryReader reader) {
|
||||
this.isRar5 = header.isRar5;
|
||||
FillBase(header);
|
||||
ReadFromReader(reader);
|
||||
ReadBytes = reader.CurrentReadByteCount;
|
||||
|
||||
int headerSizeDiff = HeaderSize - (int)ReadBytes;
|
||||
if (headerSizeDiff > 0)
|
||||
{
|
||||
reader.ReadBytes(headerSizeDiff);
|
||||
}
|
||||
|
||||
VerifyHeaderCrc(reader.GetCrc32());
|
||||
}
|
||||
|
||||
private void FillBase(RarHeader baseHeader)
|
||||
{
|
||||
@@ -15,18 +39,18 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
HeaderType = baseHeader.HeaderType;
|
||||
Flags = baseHeader.Flags;
|
||||
HeaderSize = baseHeader.HeaderSize;
|
||||
AdditionalSize = baseHeader.AdditionalSize;
|
||||
ExtraSize = baseHeader.ExtraSize;
|
||||
AdditionalDataSize = baseHeader.AdditionalDataSize;
|
||||
ReadBytes = baseHeader.ReadBytes;
|
||||
ArchiveEncoding = baseHeader.ArchiveEncoding;
|
||||
}
|
||||
|
||||
internal static RarHeader Create(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding)
|
||||
internal static RarHeader ReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5)
|
||||
{
|
||||
try
|
||||
{
|
||||
var header = new RarHeader();
|
||||
var header = new RarHeader(isRar5);
|
||||
header.ArchiveEncoding = archiveEncoding;
|
||||
reader.Mark();
|
||||
header.ReadStartFromReader(reader);
|
||||
header.ReadBytes += reader.CurrentReadByteCount;
|
||||
return header;
|
||||
@@ -39,14 +63,33 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
|
||||
private void ReadStartFromReader(RarCrcBinaryReader reader)
|
||||
{
|
||||
HeadCRC = reader.ReadUInt16();
|
||||
reader.ResetCrc();
|
||||
HeaderType = (HeaderType)reader.ReadByte();
|
||||
Flags = reader.ReadInt16();
|
||||
HeaderSize = reader.ReadInt16();
|
||||
if (FlagUtility.HasFlag(Flags, LONG_BLOCK))
|
||||
if (this.isRar5)
|
||||
{
|
||||
AdditionalSize = reader.ReadUInt32();
|
||||
HeadCRC = reader.ReadUInt32();
|
||||
reader.ResetCrc();
|
||||
HeaderSize = (int)reader.ReadRarVIntUInt32(3);
|
||||
reader.Mark();
|
||||
HeaderType = (HeaderType)reader.ReadRarVIntUInt32(1);
|
||||
Flags = reader.ReadRarVIntUInt16(2);
|
||||
if ((Flags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra)
|
||||
{
|
||||
ExtraSize = reader.ReadRarVIntUInt32();
|
||||
}
|
||||
if ((Flags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData)
|
||||
{
|
||||
AdditionalDataSize = reader.ReadRarVIntUInt32();
|
||||
}
|
||||
} else {
|
||||
reader.Mark();
|
||||
HeadCRC = reader.ReadUInt16();
|
||||
reader.ResetCrc();
|
||||
HeaderType = (HeaderType)reader.ReadByte();
|
||||
Flags = reader.ReadUInt16();
|
||||
HeaderSize = reader.ReadInt16();
|
||||
if ((Flags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData)
|
||||
{
|
||||
AdditionalDataSize = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,16 +97,14 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
//x
|
||||
internal T PromoteHeader<T>(RarCrcBinaryReader reader)
|
||||
where T : RarHeader, new()
|
||||
{
|
||||
T header = new T();
|
||||
header.FillBase(this);
|
||||
|
||||
reader.Mark();
|
||||
header.ReadFromReader(reader);
|
||||
header.ReadBytes += reader.CurrentReadByteCount;
|
||||
header.ReadBytes = reader.CurrentReadByteCount;
|
||||
|
||||
int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes;
|
||||
if (headerSizeDiff > 0)
|
||||
@@ -71,16 +112,17 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
reader.ReadBytes(headerSizeDiff);
|
||||
}
|
||||
|
||||
VerifyHeaderCrc(reader.GetCrc());
|
||||
VerifyHeaderCrc(reader.GetCrc32());
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void VerifyHeaderCrc(ushort crc)
|
||||
private void VerifyHeaderCrc(uint crc32)
|
||||
{
|
||||
if (HeaderType != HeaderType.MarkHeader)
|
||||
{
|
||||
if (crc != HeadCRC)
|
||||
var b = (this.isRar5 ? crc32 : (ushort)crc32) == HeadCRC;
|
||||
if (!b)
|
||||
{
|
||||
throw new InvalidFormatException("rar header crc mismatch");
|
||||
}
|
||||
@@ -96,22 +138,27 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
/// </summary>
|
||||
protected long ReadBytes { get; private set; }
|
||||
|
||||
protected ushort HeadCRC { get; private set; }
|
||||
protected uint HeadCRC { get; private set; }
|
||||
|
||||
public HeaderType HeaderType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Untyped flags. These should be typed when Promoting to another header
|
||||
/// </summary>
|
||||
protected short Flags { get; private set; }
|
||||
protected ushort Flags { get; private set; }
|
||||
|
||||
protected short HeaderSize { get; private set; }
|
||||
protected int HeaderSize { get; private set; }
|
||||
|
||||
internal ArchiveEncoding ArchiveEncoding { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This additional size of the header could be file data
|
||||
/// Extra header data.
|
||||
/// </summary>
|
||||
protected uint AdditionalSize { get; private set; }
|
||||
protected uint ExtraSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional data size, eg recovery record
|
||||
/// </summary>
|
||||
protected uint AdditionalDataSize { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -54,16 +54,18 @@ namespace SharpCompress.Common.Rar.Headers
|
||||
#endif
|
||||
}
|
||||
|
||||
var header = RarHeader.Create(reader, Options.ArchiveEncoding);
|
||||
var header = RarHeader.ReadBase(reader, Options.ArchiveEncoding, this.isRar5);
|
||||
if (header == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
switch (header.HeaderType)
|
||||
{
|
||||
case HeaderType.Rar5ArchiveHeader:
|
||||
case HeaderType.ArchiveHeader:
|
||||
{
|
||||
var ah = header.PromoteHeader<ArchiveHeader>(reader);
|
||||
var ah = new ArchiveHeader(header, reader);
|
||||
//x var ah = header.PromoteHeader<ArchiveHeader>(reader);
|
||||
IsEncrypted = ah.HasPassword;
|
||||
return ah;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace SharpCompress.Common.Rar {
|
||||
{
|
||||
}
|
||||
|
||||
public ushort GetCrc()
|
||||
public uint GetCrc32()
|
||||
{
|
||||
return (ushort)~currentCrc;
|
||||
return ~currentCrc;
|
||||
}
|
||||
|
||||
public void ResetCrc()
|
||||
|
||||
@@ -81,7 +81,8 @@ namespace SharpCompress.Common.Rar
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.FIRSTVOLUME);
|
||||
//return (ArchiveHeader.ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume;
|
||||
return ArchiveHeader.IsFirstVolume;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ namespace SharpCompress.Common.Rar
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME);
|
||||
return ArchiveHeader.IsVolume;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ namespace SharpCompress.Common.Rar
|
||||
get
|
||||
{
|
||||
EnsureArchiveHeaderLoaded();
|
||||
return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.SOLID);
|
||||
return ArchiveHeader.IsSolid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,18 +143,56 @@ namespace SharpCompress.IO
|
||||
int shift = 0;
|
||||
ulong result = 0;
|
||||
do {
|
||||
ulong n = ReadByte();
|
||||
byte b0 = ReadByte();
|
||||
var b1 = b0 & 0x7f;
|
||||
ulong n = (ulong)b1;
|
||||
result |= n << shift;
|
||||
shift += 7;
|
||||
if ((n & 0x80) == 0) {
|
||||
if (b0 == b1) {
|
||||
return result;
|
||||
}
|
||||
// note: we're actually only allowing a max high bit of 2^56
|
||||
// to avoid an extra complex check for shift overflow due to the
|
||||
// 10th byte having high bits set
|
||||
shift += 7;
|
||||
} while (shift < 63);
|
||||
|
||||
throw new FormatException("malformed vint");
|
||||
}
|
||||
|
||||
public uint ReadRarVIntUInt32(int maxBytes = 5) {
|
||||
int shift = 0;
|
||||
uint result = 0;
|
||||
do {
|
||||
byte b0 = ReadByte();
|
||||
var b1 = b0 & 0x7f;
|
||||
uint n = (uint)b1;
|
||||
result |= n << shift;
|
||||
if (b0 == b1) {
|
||||
return result;
|
||||
}
|
||||
shift += 7;
|
||||
// NOTE: we are too strict here but handling the full range adds complexity and we don't need it
|
||||
} while (shift < 28 && --maxBytes > 0);
|
||||
|
||||
throw new FormatException("malformed vint");
|
||||
}
|
||||
|
||||
public ushort ReadRarVIntUInt16(int maxBytes = 3) {
|
||||
int shift = 0;
|
||||
uint result = 0;
|
||||
do {
|
||||
byte b0 = ReadByte();
|
||||
var b1 = b0 & 0x7f;
|
||||
uint n = (uint)b1;
|
||||
result |= n << shift;
|
||||
if (b0 == b1) {
|
||||
return checked((ushort)result);
|
||||
}
|
||||
shift += 7;
|
||||
// NOTE: we are too strict here but handling the full range adds complexity and we don't need it
|
||||
} while (shift < 14 && --maxBytes > 0);
|
||||
|
||||
throw new FormatException("malformed vint");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user