#ifndef _RAR_HEADERS_
#define _RAR_HEADERS_

#define  SIZEOF_MARKHEAD3        7 // Size of RAR 4.x archive mark header.
#define  SIZEOF_MAINHEAD14       7 // Size of RAR 1.4 main archive header.
#define  SIZEOF_MAINHEAD3       13 // Size of RAR 4.x main archive header.
#define  SIZEOF_FILEHEAD14      21 // Size of RAR 1.4 file header.
#define  SIZEOF_FILEHEAD3       32 // Size of RAR 3.0 file header.
#define  SIZEOF_SHORTBLOCKHEAD   7 // Smallest RAR 4.x block size.
#define  SIZEOF_LONGBLOCKHEAD   11
#define  SIZEOF_SUBBLOCKHEAD    14
#define  SIZEOF_COMMHEAD        13
#define  SIZEOF_PROTECTHEAD     26
#define  SIZEOF_STREAMHEAD      26

#define  VER_PACK               29U
#define  VER_PACK5              50U // It is stored as 0, but we subtract 50 when saving an archive.
#define  VER_PACK7              70U // It is stored as 1, but we subtract 70 when saving an archive.
#define  VER_UNPACK             29U
#define  VER_UNPACK5            50U // It is stored as 0, but we add 50 when reading an archive.
#define  VER_UNPACK7            70U // It is stored as 1, but we add 50 when reading an archive.
#define  VER_UNKNOWN          9999U // Just some large value.

#define  MHD_VOLUME         0x0001U

// Old style main archive comment embed into main archive header. Must not
// be used in new archives anymore.
#define  MHD_COMMENT        0x0002U

#define  MHD_LOCK           0x0004U
#define  MHD_SOLID          0x0008U
#define  MHD_PACK_COMMENT   0x0010U
#define  MHD_NEWNUMBERING   0x0010U
#define  MHD_AV             0x0020U
#define  MHD_PROTECT        0x0040U
#define  MHD_PASSWORD       0x0080U
#define  MHD_FIRSTVOLUME    0x0100U

#define  LHD_SPLIT_BEFORE   0x0001U
#define  LHD_SPLIT_AFTER    0x0002U
#define  LHD_PASSWORD       0x0004U

// Old style file comment embed into file header. Must not be used
// in new archives anymore.
#define  LHD_COMMENT        0x0008U

// For non-file subheaders it denotes 'subblock having a parent file' flag.
#define  LHD_SOLID          0x0010U


#define  LHD_WINDOWMASK     0x00e0U
#define  LHD_WINDOW64       0x0000U
#define  LHD_WINDOW128      0x0020U
#define  LHD_WINDOW256      0x0040U
#define  LHD_WINDOW512      0x0060U
#define  LHD_WINDOW1024     0x0080U
#define  LHD_WINDOW2048     0x00a0U
#define  LHD_WINDOW4096     0x00c0U
#define  LHD_DIRECTORY      0x00e0U

#define  LHD_LARGE          0x0100U
#define  LHD_UNICODE        0x0200U
#define  LHD_SALT           0x0400U
#define  LHD_VERSION        0x0800U
#define  LHD_EXTTIME        0x1000U

#define  SKIP_IF_UNKNOWN    0x4000U
#define  LONG_BLOCK         0x8000U

#define  EARC_NEXT_VOLUME   0x0001U // Not last volume.
#define  EARC_DATACRC       0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
#define  EARC_REVSPACE      0x0004U // Reserve space for end of REV file 7 byte record.
#define  EARC_VOLNUMBER     0x0008U // Store a number of current volume.

enum HEADER_TYPE {
  // RAR 5.0 header types.
  HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
  HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,

  // RAR 1.5 - 4.x header types.
  HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
  HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
  HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
};


// RAR 2.9 and earlier service haeders, mostly outdated and not supported.
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
       NTACL_HEAD=0x104,STREAM_HEAD=0x105 };


// Internal implementation, depends on archive format version.
enum HOST_SYSTEM {
  // RAR 5.0 host OS
  HOST5_WINDOWS=0,HOST5_UNIX=1,

  // RAR 3.0 host OS.
  HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
  HOST_BEOS=5,HOST_MAX
};

// Unified archive format independent implementation.
enum HOST_SYSTEM_TYPE {
  HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
};


// We also use these values in extra field, so do not modify them.
enum FILE_SYSTEM_REDIRECT {
  FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
  FSREDIR_HARDLINK, FSREDIR_FILECOPY
};


#define SUBHEAD_TYPE_CMT      L"CMT"
#define SUBHEAD_TYPE_QOPEN    L"QO"
#define SUBHEAD_TYPE_ACL      L"ACL"
#define SUBHEAD_TYPE_STREAM   L"STM"
#define SUBHEAD_TYPE_UOWNER   L"UOW"
#define SUBHEAD_TYPE_AV       L"AV"
#define SUBHEAD_TYPE_RR       L"RR"
#define SUBHEAD_TYPE_OS2EA    L"EA2"

/* new file inherits a subblock when updating a host file */
#define SUBHEAD_FLAGS_INHERITED    0x80000000

#define SUBHEAD_FLAGS_CMT_UNICODE  0x00000001


struct MarkHeader
{
  byte Mark[8];

  // Following fields are virtual and not present in real blocks.
  uint HeadSize;
};


struct BaseBlock
{
  uint HeadCRC;  // 'ushort' for RAR 1.5.
  HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
  uint Flags;    // 'ushort' for RAR 1.5.
  uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.

  bool SkipIfUnknown;

  void Reset()
  {
    SkipIfUnknown=false;
  }

  // We use it to assign this block data to inherited blocks.
  // Such function seems to be cleaner than '(BaseBlock&)' cast or adding
  // 'using BaseBlock::operator=;' to every inherited header.
  void SetBaseBlock(BaseBlock &Src)
  {
    *this=Src;
  }
};


struct BlockHeader:BaseBlock
{
  uint DataSize;
};


struct MainHeader:BaseBlock
{
  ushort HighPosAV;
  uint PosAV;
  bool CommentInHeader;
  bool PackComment;     // For RAR 1.4 archive format only.
  bool Locator;
  uint64 QOpenOffset;   // Offset of quick list record.
  uint64 QOpenMaxSize;  // Maximum size of QOpen offset in locator extra field.
  uint64 RROffset;      // Offset of recovery record.
  uint64 RRMaxSize;     // Maximum size of RR offset in locator extra field.
  size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field.
  std::wstring OrigName;  // Original archive name.
  RarTime OrigTime;       // Original archive time.

  void Reset();
};


struct FileHeader:BlockHeader
{
  byte HostOS;
  uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
  byte Method;
  union {
    uint FileAttr;
    uint SubFlags;
  };
  std::wstring FileName;

  std::vector<byte> SubData;

  RarTime mtime;
  RarTime ctime;
  RarTime atime;

  int64 PackSize;
  int64 UnpSize;
  int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.

  HashValue FileHash;

  uint FileFlags;

  bool SplitBefore;
  bool SplitAfter;

  bool UnknownUnpSize;

  bool Encrypted;
  CRYPT_METHOD CryptMethod;
  bool SaltSet;
  byte Salt[SIZE_SALT50];
  byte InitV[SIZE_INITV];
  bool UsePswCheck;
  byte PswCheck[SIZE_PSWCHECK];

  // Use HMAC calculated from HashKey and checksum instead of plain checksum.
  bool UseHashKey;

  // Key to convert checksum to HMAC. Derived from password with PBKDF2
  // using additional iterations.
  byte HashKey[SHA256_DIGEST_SIZE];

  uint Lg2Count; // Log2 of PBKDF2 repetition count.

  bool Solid;
  bool Dir;
  bool CommentInHeader; // RAR 2.0 file comment.
  bool Version;   // name.ext;ver file name containing the version number.
  uint64 WinSize;
  bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).

  // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
  bool LargeFile;
  
  // 'true' for HEAD_SERVICE block, which is a child of preceding file block.
  // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives.
  bool SubBlock;

  HOST_SYSTEM_TYPE HSType;

  FILE_SYSTEM_REDIRECT RedirType;
  std::wstring RedirName;
  bool DirTarget;

  bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
  char UnixOwnerName[256],UnixGroupName[256];
#ifdef _UNIX
  uid_t UnixOwnerID;
  gid_t UnixGroupID;
#else // Need these Unix fields in Windows too for 'list' command.
  uint UnixOwnerID;
  uint UnixGroupID;
#endif

  void Reset(size_t SubDataSize=0);

  bool CmpName(const wchar *Name)
  {
    return FileName==Name;
  }

//  FileHeader& operator = (FileHeader &hd);
};


struct EndArcHeader:BaseBlock
{
  // Optional CRC32 of entire archive up to start of EndArcHeader block.
  // Present in RAR 4.x archives if EARC_DATACRC flag is set.
  uint ArcDataCRC;  
  
  uint VolNumber; // Optional number of current volume.

  // 7 additional zero bytes can be stored here if EARC_REVSPACE is set.

  bool NextVolume; // Not last volume.
  bool DataCRC;
  bool RevSpace;
  bool StoreVolNumber;
  void Reset()
  {
    BaseBlock::Reset();
    NextVolume=false;
    DataCRC=false;
    RevSpace=false;
    StoreVolNumber=false;
  }
};


struct CryptHeader:BaseBlock
{
  bool UsePswCheck;
  uint Lg2Count; // Log2 of PBKDF2 repetition count.
  byte Salt[SIZE_SALT50];
  byte PswCheck[SIZE_PSWCHECK];
};


// SubBlockHeader and its successors were used in RAR 2.x format.
// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
struct SubBlockHeader:BlockHeader
{
  ushort SubType;
  byte Level;
};


struct CommentHeader:BaseBlock
{
  ushort UnpSize;
  byte UnpVer;
  byte Method;
  ushort CommCRC;
};


struct ProtectHeader:BlockHeader
{
  byte Version;
  ushort RecSectors;
  uint TotalBlocks;
  byte Mark[8];
};


struct EAHeader:SubBlockHeader
{
  uint UnpSize;
  byte UnpVer;
  byte Method;
  uint EACRC;
};


struct StreamHeader:SubBlockHeader
{
  uint UnpSize;
  byte UnpVer;
  byte Method;
  uint StreamCRC;
  ushort StreamNameSize;
  std::string StreamName;
};


#endif
