diff --git a/SabreTools.Library/FileTypes/CoreRarArchive.cs b/SabreTools.Library/FileTypes/CoreRarArchive.cs index 34c31118..69a5967a 100644 --- a/SabreTools.Library/FileTypes/CoreRarArchive.cs +++ b/SabreTools.Library/FileTypes/CoreRarArchive.cs @@ -1,10 +1,119 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using SabreTools.Library.Data; /// -/// http://www.rarlab.com/technote.htm#srvheaders +/// This code is based on the header format described at http://www.rarlab.com/technote.htm#srvheaders /// +/// +/// --------------------------------------------- +/// vint +/// +/// Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data +/// and highest bit in every byte is the continuation flag.If highest bit is 0, this is the last byte in sequence. +/// So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present, +/// contains next 7 bits and so on. +/// +/// Currently RAR format uses vint to store up to 64 bit integers, resulting in 10 bytes maximum. This value may +/// be increased in the future if necessary for some reason. +/// +/// Sometimes RAR needs to pre-allocate space for vint before knowing its exact value. In such situation it can +/// allocate more space than really necessary and then fill several leading bytes with 0x80 hexadecimal, which means +/// 0 with continuation flag set. +/// ---------------------------------------------- +/// General archive layout: +/// +/// Self-extracting module(optional) (RAR assumes the maximum SFX module size to not exceed 1 MB, but this value +/// can be increased in the future. +/// RAR 5.0 signature (RAR 5.0 signature consists of 8 bytes: 0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00. +/// You need to search for this signature in supposed archive from beginning and up to maximum SFX +/// module size. Just for comparison this is RAR 4.x 7 byte length signature: 0x52 0x61 0x72 0x21 0x1A 0x07 0x00.) +/// Archive encryption header(optional) +/// Main archive header +/// Archive comment service header(optional) +/// File header 1 +/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional). +/// ... +/// File header N +/// Service headers(NTFS ACL, streams, etc.) for preceding file(optional). +/// Recovery record(optional). +/// End of archive header. +/// ---------------------------------------------- +/// General archive block format: +/// +/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.) +/// Header size: vint (Size of header data starting from Header type field and up to and including the optional extra area. +/// This field must not be longer than 3 bytes in current implementation, resulting in 2 MB maximum header size.) +/// Header type: vint (Type of archive header. Possible values are: ) +/// 1   Main archive header. +/// 2   File header. +/// 3   Service header. +/// 4   Archive encryption header. +/// 5   End of archive header. +/// Header flags: vint (Flags common for all headers:) +/// 0x0001   Extra area is present in the end of header. +/// 0x0002   Data area is present in the end of header. +/// 0x0004   Blocks with unknown type and this flag must be skipped when updating an archive. +/// 0x0008   Data area is continuing from previous volume. +/// 0x0010   Data area is continuing in next volume. +/// 0x0020   Block depends on preceding file block. +/// 0x0040   Preserve a child block if host block is modified. +/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.) +/// Data size: vint (Size of data area. Optional field, present only if 0x0002 header flag is set.) +/// ...: ... (Fields specific for current block type. See concrete block type descriptions for details) +/// Extra data: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.) +/// Data area: vint (Optional data area, present only if 0x0002 header flag is set. Used to store large data amounts, such as +/// compressed file data. Not counted in Header CRC and Header size fields. +/// ---------------------------------------------- +/// General extra area format +/// +/// Size: vint (Size of record data starting from Type.) +/// Type: vint (Record type. Different archive blocks have different associated extra area record types. Read the +/// concrete archive block description for details. New record types can be added in the future, so unknown +/// record types need to be skipped without interrupting an operation.) +/// Data: ... (Record dependent data. May be missing if record consists only from size and type.) +/// ---------------------------------------------- +/// Archive encryption header: +/// +/// Header CRC32: uint32 +/// Header size: vint +/// Header type: vint (4) +/// Header flags: vint +/// Encryption version: vint (Version of encryption algorithm. Now only 0 version(AES-256) is supported.) +/// Encryption flags: vint +/// 0x0001   Password check data is present. +/// KDF count: 1 byte (Binary logarithm of iteration number for PBKDF2 function.RAR can refuse to process +/// KDF count exceeding some threshold. Concrete value of threshold is a version dependent.) +/// Salt: 16 bytes (Salt value used globally for all encrypted archive headers.) +/// Check value: 12 bytes (Value used to verify the password validity. Present only if 0x0001 encryption +/// flag is set.First 8 bytes are calculated using additional PBKDF2 rounds, 4 last bytes is the additional +/// checksum. Together with the standard header CRC32 we have 64 bit checksum to reliably verify this field +/// integrity and distinguish invalid password and damaged data. Further details can be found in UnRAR source code.) +/// ---------------------------------------------- +/// Main archive header: +/// +/// Header CRC32: uint32 (CRC32 of header data starting from Header size field and up to and including the optional extra area.) +/// Header size: vint (Size of header data starting from Header type field and up to and including the optional extra area. This field must not be longer than 3 bytes in current implementation, resulting in 2 MB maximum header size.) +/// Header type: vint (1) +/// Header flags: vint (Flags common for all headers) +/// Extra area size: vint (Size of extra area. Optional field, present only if 0x0001 header flag is set.) +/// Archive flags: vint +/// 0x0001   Volume.Archive is a part of multivolume set. +/// 0x0002   Volume number field is present.This flag is present in all volumes except first. +/// 0x0004   Solid archive. +/// 0x0008   Recovery record is present. +/// 0x0010   Locked archive. +/// Volume number: vint (Optional field, present only if 0x0002 archive flag is set. Not present for first volume, +/// 1 for second volume, 2 for third and so on.) +/// Extra area: ... (Optional area containing additional header fields, present only if 0x0001 header flag is set.) +/// [Extra area of main archive header can contain following record types +/// Type Name Description +/// 0x01 Locator Contains positions of different service blocks, so they can be accessed quickly, without scanning +/// the entire archive.This record is optional.If it is missing, it is still necessary to scan the entire archive +/// to verify presence of service blocks.] +/// ---------------------------------------------- +/// namespace SabreTools.Library.FileTypes { public class CoreRarArchive @@ -110,4 +219,71 @@ namespace SabreTools.Library.FileTypes public uint ServiceSize; // vint public byte[] ServiceData; } + + // BELOW ARE CONCRETE IMPLEMENTATIONS OF HEADER DETAILS + + /// + /// General archive block format used by all RAR block types + /// + public class GeneralArchiveBlockFormat + { + public uint HeaderCRC32; + public uint HeaderSize; // vint + public HeaderType HeaderType; + public HeaderFlags HeaderFlags; + public ulong ExtraAreaSize; // vint + public ulong DataAreaSize; // vint + public byte[] ExtraArea; + public byte[] DataArea; + } + + /// + /// General extra area format used by all RAR extra area records + /// + public class GeneralExtraAreaFormat + { + public ulong Size; // vint + public ulong Type; // vint + public byte[] Data; + } + + /// + /// Encryption header only present in encrypted archives + /// + /// Every proceeding header is started from 16 byte AES-256 + /// initialization vectors followed by encrypted header data + /// + public class ArchiveEncryptionHeader : GeneralArchiveBlockFormat + { + public new HeaderType HeaderType = HeaderType.ArchiveEncryptionHeader; + public ulong EncryptionVersion; // vint + public ulong EncryptionFlags; // vint + } + + /// + /// Types of archive header + /// + public enum HeaderType : ulong // vint + { + MainArchiveHeader = 1, + FileHeader = 2, + ServiceHeader = 3, + ArchiveEncryptionHeader = 4, + EndOfArchiveHeader = 5, + } + + /// + /// Flags common for all headers + /// + [Flags] + public enum HeaderFlags : ulong // vint + { + ExtraAreaIsPresentInEndOfHeader = 0x0001, + DataAreaIsPresentInEndOfHeader = 0x0002, + BlocksWithUnknownType = 0x0004, // this flag must be skipped when updating an archive + DataAreaIsContinuingFromPreviousVolume = 0x0008, + DataAreaIsContinuingInNextVolume = 0x0010, + BlockDependsOnPrecedingFileBlock = 0x0020, + PreserveChildBlockIfHostBlockIsModified = 0x0040, + } }