diff --git a/UnRAR.vcproj b/UnRAR.vcproj
new file mode 100644
index 0000000..a524f4f
--- /dev/null
+++ b/UnRAR.vcproj
@@ -0,0 +1,643 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UnRARDll.vcproj b/UnRARDll.vcproj
new file mode 100644
index 0000000..cc47f8f
--- /dev/null
+++ b/UnRARDll.vcproj
@@ -0,0 +1,876 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/acknow.txt b/acknow.txt
new file mode 100644
index 0000000..b982ece
--- /dev/null
+++ b/acknow.txt
@@ -0,0 +1,92 @@
+ ACKNOWLEDGMENTS
+
+* We used "Screaming Fast Galois Field Arithmetic Using Intel
+ SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
+ and Ethan L. Miller to improve Reed-Solomon coding performance.
+ Also we are grateful to Artem Drobanov and Bulat Ziganshin
+ for samples and ideas allowed to make Reed-Solomon coding
+ more efficient.
+
+* RAR text compression algorithm is based on Dmitry Shkarin PPMII
+ and Dmitry Subbotin carryless rangecoder public domain source code.
+ You may find it in ftp.elf.stuba.sk/pub/pc/pack.
+
+* RAR encryption includes parts of code from Szymon Stefanek
+ and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
+
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+
+ Source code of this package also as other cryptographic technology
+ and computing project related links are available on Brian Gladman's
+ web site: http://www.gladman.me.uk
+
+* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm.
+ Original Intel Slicing-by-8 code is available here:
+
+ http://sourceforge.net/projects/slicing-by-8/
+
+ Original Intel Slicing-by-8 code is licensed under BSD License
+ available at http://www.opensource.org/licenses/bsd-license.html
+
+ Copyright (c) 2004-2006 Intel Corporation.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with
+ the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
+ designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
+ and Christian Winnerlein.
+
+* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
+ to significantly improve RAR compression and speed.
diff --git a/arccmt.cpp b/arccmt.cpp
new file mode 100644
index 0000000..8789f60
--- /dev/null
+++ b/arccmt.cpp
@@ -0,0 +1,162 @@
+static bool IsAnsiEscComment(const wchar *Data,size_t Size);
+
+bool Archive::GetComment(Array *CmtData)
+{
+ if (!MainComment)
+ return false;
+ SaveFilePos SavePos(*this);
+
+#ifndef SFX_MODULE
+ ushort CmtLength;
+ if (Format==RARFMT14)
+ {
+ Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
+ CmtLength=GetByte();
+ CmtLength+=(GetByte()<<8);
+ }
+ else
+#endif
+ {
+ if (MainHead.CommentInHeader)
+ {
+ // Old style (RAR 2.9) archive comment embedded into the main
+ // archive header.
+ Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
+ ReadHeader();
+ }
+ else
+ {
+ // Current (RAR 3.0+) version of archive comment.
+ Seek(GetStartPos(),SEEK_SET);
+ return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
+ }
+#ifndef SFX_MODULE
+ // Old style (RAR 2.9) comment header embedded into the main
+ // archive header.
+ if (BrokenHeader)
+ {
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
+ }
+ CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD;
+#endif
+ }
+#ifndef SFX_MODULE
+ if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30)
+ {
+ if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
+ return(false);
+ ComprDataIO DataIO;
+ DataIO.SetTestMode(true);
+ uint UnpCmtLength;
+ if (Format==RARFMT14)
+ {
+#ifdef RAR_NOCRYPT
+ return(false);
+#else
+ UnpCmtLength=GetByte();
+ UnpCmtLength+=(GetByte()<<8);
+ CmtLength-=2;
+ DataIO.SetCmt13Encryption();
+ CommHead.UnpVer=15;
+#endif
+ }
+ else
+ UnpCmtLength=CommHead.UnpSize;
+ DataIO.SetFiles(this,NULL);
+ DataIO.EnableShowProgress(false);
+ DataIO.SetPackedSizeToRead(CmtLength);
+ DataIO.UnpHash.Init(HASH_CRC32,1);
+
+ Unpack CmtUnpack(&DataIO);
+ CmtUnpack.Init(0x10000,false);
+ CmtUnpack.SetDestSize(UnpCmtLength);
+ CmtUnpack.DoUnpack(CommHead.UnpVer,false);
+
+ if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
+ {
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
+ }
+ else
+ {
+ byte *UnpData;
+ size_t UnpDataSize;
+ DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
+#ifdef _WIN_ALL
+ OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
+#endif
+ CmtData->Alloc(UnpDataSize+1);
+ memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
+ CharToWide((char *)UnpData,CmtData->Addr(0),UnpDataSize);
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
+ }
+ }
+ else
+ {
+ Array CmtRaw(CmtLength);
+ Read(&CmtRaw[0],CmtLength);
+
+ if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
+ {
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
+ }
+ CmtData->Alloc(CmtLength+1);
+ CmtRaw.Push(0);
+#ifdef _WIN_ALL
+ OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
+#endif
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtLength);
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
+ }
+#endif
+ return CmtData->Size() > 0;
+}
+
+
+bool Archive::ReadCommentData(Array *CmtData)
+{
+ Array CmtRaw;
+ if (!ReadSubData(&CmtRaw,NULL))
+ return false;
+ size_t CmtSize=CmtRaw.Size();
+ CmtRaw.Push(0);
+ CmtData->Alloc(CmtSize+1);
+ if (Format==RARFMT50)
+ UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ else
+ if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
+ {
+ RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
+ (*CmtData)[CmtSize/2]=0;
+
+ }
+ else
+ {
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ }
+ CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
+ return true;
+}
+
+
+void Archive::ViewComment()
+{
+#ifndef GUI
+ if (Cmd->DisableComment)
+ return;
+ Array CmtBuf;
+ if (GetComment(&CmtBuf))
+ {
+ size_t CmtSize=CmtBuf.Size();
+ wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
+ if (ChPtr!=NULL)
+ CmtSize=ChPtr-&CmtBuf[0];
+ mprintf(L"\n");
+ OutComment(&CmtBuf[0],CmtSize);
+ }
+#endif
+}
+
+
diff --git a/archive.cpp b/archive.cpp
new file mode 100644
index 0000000..203899f
--- /dev/null
+++ b/archive.cpp
@@ -0,0 +1,336 @@
+#include "rar.hpp"
+
+#ifndef SHELL_EXT
+#include "arccmt.cpp"
+#endif
+
+
+Archive::Archive(RAROptions *InitCmd)
+{
+ Cmd=NULL; // Just in case we'll have an exception in 'new' below.
+
+ DummyCmd=(InitCmd==NULL);
+ Cmd=DummyCmd ? (new RAROptions):InitCmd;
+
+ OpenShared=Cmd->OpenShared;
+ Format=RARFMT15;
+ Solid=false;
+ Volume=false;
+ MainComment=false;
+ Locked=false;
+ Signed=false;
+ FirstVolume=false;
+ NewNumbering=false;
+ SFXSize=0;
+ LatestTime.Reset();
+ Protected=false;
+ Encrypted=false;
+ FailedHeaderDecryption=false;
+ BrokenHeader=false;
+ LastReadBlock=0;
+
+ CurBlockPos=0;
+ NextBlockPos=0;
+
+ RecoverySize=-1;
+ RecoveryPercent=-1;
+
+ memset(&MainHead,0,sizeof(MainHead));
+ memset(&CryptHead,0,sizeof(CryptHead));
+ memset(&EndArcHead,0,sizeof(EndArcHead));
+
+ VolNumber=0;
+ VolWrite=0;
+ AddingFilesSize=0;
+ AddingHeadersSize=0;
+ *FirstVolumeName=0;
+
+ Splitting=false;
+ NewArchive=false;
+
+ SilentOpen=false;
+
+}
+
+
+Archive::~Archive()
+{
+ if (DummyCmd)
+ delete Cmd;
+}
+
+
+#ifndef SHELL_EXT
+void Archive::CheckArc(bool EnableBroken)
+{
+ if (!IsArchive(EnableBroken))
+ {
+ // If FailedHeaderDecryption is set, we already reported that archive
+ // password is incorrect.
+ if (!FailedHeaderDecryption)
+ uiMsg(UIERROR_BADARCHIVE,FileName);
+ ErrHandler.Exit(RARX_FATAL);
+ }
+}
+#endif
+
+
+#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
+void Archive::CheckOpen(const wchar *Name)
+{
+ TOpen(Name);
+ CheckArc(false);
+}
+#endif
+
+
+bool Archive::WCheckOpen(const wchar *Name)
+{
+ if (!WOpen(Name))
+ return false;
+ if (!IsArchive(false))
+ {
+ uiMsg(UIERROR_BADARCHIVE,FileName);
+ Close();
+ return false;
+ }
+ return true;
+}
+
+
+RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
+{
+ RARFORMAT Type=RARFMT_NONE;
+ if (Size>=1 && D[0]==0x52)
+#ifndef SFX_MODULE
+ if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
+ Type=RARFMT14;
+ else
+#endif
+ if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
+ {
+ // We check the last signature byte, so we can return a sensible
+ // warning in case we'll want to change the archive format
+ // sometimes in the future.
+ if (D[6]==0)
+ Type=RARFMT15;
+ else
+ if (D[6]==1)
+ Type=RARFMT50;
+ else
+ if (D[6]==2)
+ Type=RARFMT_FUTURE;
+ }
+ return Type;
+}
+
+
+bool Archive::IsArchive(bool EnableBroken)
+{
+ Encrypted=false;
+ BrokenHeader=false; // Might be left from previous volume.
+
+#ifndef SFX_MODULE
+ if (IsDevice())
+ {
+ uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
+ return false;
+ }
+#endif
+ if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
+ return false;
+ SFXSize=0;
+
+ RARFORMAT Type;
+ if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
+ {
+ Format=Type;
+ if (Format==RARFMT14)
+ Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
+ }
+ else
+ {
+ Array Buffer(MAXSFXSIZE);
+ long CurPos=(long)Tell();
+ int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
+ for (int I=0;I0 && CurPos<28 && ReadSize>31)
+ {
+ char *D=&Buffer[28-CurPos];
+ if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
+ continue;
+ }
+ SFXSize=CurPos+I;
+ Seek(SFXSize,SEEK_SET);
+ if (Format==RARFMT15 || Format==RARFMT50)
+ Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
+ break;
+ }
+ if (SFXSize==0)
+ return false;
+ }
+ if (Format==RARFMT_FUTURE)
+ {
+ uiMsg(UIERROR_NEWRARFORMAT,FileName);
+ return false;
+ }
+ if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
+ {
+ Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1);
+ if (MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
+ return false;
+ MarkHead.HeadSize=SIZEOF_MARKHEAD5;
+ }
+ else
+ MarkHead.HeadSize=SIZEOF_MARKHEAD3;
+
+#ifdef RARDLL
+ // If callback function is not set, we cannot get the password,
+ // so we skip the initial header processing for encrypted header archive.
+ // It leads to skipped archive comment, but the rest of archive data
+ // is processed correctly.
+ if (Cmd->Callback==NULL)
+ SilentOpen=true;
+#endif
+
+ // Skip the archive encryption header if any and read the main header.
+ while (ReadHeader()!=0)
+ {
+ HEADER_TYPE Type=GetHeaderType();
+ // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
+ // avoid the password prompt.
+ if (Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT)
+ break;
+ SeekToNext();
+ }
+
+ // This check allows to make RS based recovery even if password is incorrect.
+ // But we should not do it for EnableBroken or we'll get 'not RAR archive'
+ // messages when extracting encrypted archives with wrong password.
+ if (FailedHeaderDecryption && !EnableBroken)
+ return false;
+
+ SeekToNext();
+ if (BrokenHeader) // Main archive header is corrupt.
+ {
+ uiMsg(UIERROR_MHEADERBROKEN,FileName);
+ if (!EnableBroken)
+ return false;
+ }
+
+ MainComment=MainHead.CommentInHeader;
+
+ // If we process non-encrypted archive or can request a password,
+ // we set 'first volume' flag based on file attributes below.
+ // It is necessary for RAR 2.x archives, which did not have 'first volume'
+ // flag in main header. Also for all RAR formats we need to scan until
+ // first file header to set "comment" flag when reading service header.
+ // Unless we are in silent mode, we need to know about presence of comment
+ // immediately after IsArchive call.
+ if (!SilentOpen || !Encrypted)
+ {
+ SaveFilePos SavePos(*this);
+ int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
+
+ while (ReadHeader()!=0)
+ {
+ HEADER_TYPE HeaderType=GetHeaderType();
+ if (HeaderType==HEAD_SERVICE)
+ {
+ // If we have a split service headers, it surely indicates non-first
+ // volume. But not split service header does not guarantee the first
+ // volume, because we can have split file after non-split archive
+ // comment. So we do not quit from loop here.
+ FirstVolume=Volume && !SubHead.SplitBefore;
+ }
+ else
+ if (HeaderType==HEAD_FILE)
+ {
+ FirstVolume=Volume && !FileHead.SplitBefore;
+ break;
+ }
+ else
+ if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
+ break;
+ SeekToNext();
+ }
+ CurBlockPos=SaveCurBlockPos;
+ NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ }
+ if (!Volume || FirstVolume)
+ wcscpy(FirstVolumeName,FileName);
+
+ return true;
+}
+
+
+
+
+void Archive::SeekToNext()
+{
+ Seek(NextBlockPos,SEEK_SET);
+}
+
+
+
+
+
+
+// Calculate the block size including encryption fields and padding if any.
+uint Archive::FullHeaderSize(size_t Size)
+{
+ if (Encrypted)
+ {
+ Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
+ if (Format == RARFMT50)
+ Size += SIZE_INITV;
+ else
+ Size += SIZE_SALT30;
+ }
+ return uint(Size);
+}
+
+
+
+
+#ifdef USE_QOPEN
+bool Archive::Open(const wchar *Name,uint Mode)
+{
+ // Important if we reuse Archive object and it has virtual QOpen
+ // file position not matching real. For example, for 'l -v volname'.
+ QOpen.Unload();
+
+ return File::Open(Name,Mode);
+}
+
+
+int Archive::Read(void *Data,size_t Size)
+{
+ size_t Result;
+ if (QOpen.Read(Data,Size,Result))
+ return (int)Result;
+ return File::Read(Data,Size);
+}
+
+
+void Archive::Seek(int64 Offset,int Method)
+{
+ if (!QOpen.Seek(Offset,Method))
+ File::Seek(Offset,Method);
+}
+
+
+int64 Archive::Tell()
+{
+ int64 QPos;
+ if (QOpen.Tell(&QPos))
+ return QPos;
+ return File::Tell();
+}
+#endif
+
diff --git a/archive.hpp b/archive.hpp
new file mode 100644
index 0000000..6d15156
--- /dev/null
+++ b/archive.hpp
@@ -0,0 +1,150 @@
+#ifndef _RAR_ARCHIVE_
+#define _RAR_ARCHIVE_
+
+class PPack;
+class RawRead;
+class RawWrite;
+
+enum NOMODIFY_FLAGS
+{
+ NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
+};
+
+enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
+
+enum ADDSUBDATA_FLAGS
+{
+ ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
+ ASDF_COMPRESS = 2, // Allow to compress data following subheader.
+ ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
+ ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
+};
+
+class Archive:public File
+{
+ private:
+ void UpdateLatestTime(FileHeader *CurBlock);
+ void ConvertNameCase(wchar *Name);
+ void ConvertFileHeader(FileHeader *hd);
+ void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite);
+ size_t ReadHeader14();
+ size_t ReadHeader15();
+ size_t ReadHeader50();
+ void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
+ void RequestArcPassword();
+ void UnexpEndArcMsg();
+ void BrokenHeaderMsg();
+ void UnkEncVerMsg(const wchar *Name);
+ void UnkEncVerMsg();
+ bool ReadCommentData(Array *CmtData);
+
+#if !defined(SHELL_EXT) && !defined(RAR_NOCRYPT)
+ CryptData HeadersCrypt;
+#endif
+#ifndef SHELL_EXT
+ ComprDataIO SubDataIO;
+#endif
+ bool DummyCmd;
+ RAROptions *Cmd;
+
+ int64 RecoverySize;
+ int RecoveryPercent;
+
+ RarTime LatestTime;
+ int LastReadBlock;
+ HEADER_TYPE CurHeaderType;
+
+ bool SilentOpen;
+#ifdef USE_QOPEN
+ QuickOpen QOpen;
+#endif
+ public:
+ Archive(RAROptions *InitCmd=NULL);
+ ~Archive();
+ static RARFORMAT IsSignature(const byte *D,size_t Size);
+ bool IsArchive(bool EnableBroken);
+ size_t SearchBlock(HEADER_TYPE HeaderType);
+ size_t SearchSubBlock(const wchar *Type);
+ size_t SearchRR();
+ void WriteBlock(HEADER_TYPE HeaderType,BaseBlock *wb=NULL,bool OnlySetSize=false,bool NonFinalWrite=false);
+ void SetBlockSize(HEADER_TYPE HeaderType,BaseBlock *wb=NULL) {WriteBlock(HeaderType,wb,true);}
+ size_t ReadHeader();
+ void CheckArc(bool EnableBroken);
+ void CheckOpen(const wchar *Name);
+ bool WCheckOpen(const wchar *Name);
+ bool GetComment(Array *CmtData);
+ void ViewComment();
+ void SetLatestTime(RarTime *NewTime);
+ void SeekToNext();
+ bool CheckAccess();
+ bool IsArcDir();
+ void ConvertAttributes();
+ void VolSubtractHeaderSize(size_t SubSize);
+ uint FullHeaderSize(size_t Size);
+ int64 GetStartPos();
+ void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
+ const wchar *Name,uint Flags);
+ bool ReadSubData(Array *UnpData,File *DestFile);
+ HEADER_TYPE GetHeaderType() {return CurHeaderType;};
+ RAROptions* GetRAROptions() {return Cmd;}
+ void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
+#ifdef USE_QOPEN
+ bool Open(const wchar *Name,uint Mode=FMF_READ);
+ int Read(void *Data,size_t Size);
+ void Seek(int64 Offset,int Method);
+ int64 Tell();
+ void QOpenUnload() {QOpen.Unload();}
+#endif
+
+ BaseBlock ShortBlock;
+ MarkHeader MarkHead;
+ MainHeader MainHead;
+ CryptHeader CryptHead;
+ FileHeader FileHead;
+ EndArcHeader EndArcHead;
+ SubBlockHeader SubBlockHead;
+ FileHeader SubHead;
+ CommentHeader CommHead;
+ ProtectHeader ProtectHead;
+ AVHeader AVHead;
+ SignHeader SignHead;
+ UnixOwnersHeader UOHead;
+ MacFInfoHeader MACHead;
+ EAHeader EAHead;
+ StreamHeader StreamHead;
+
+ int64 CurBlockPos;
+ int64 NextBlockPos;
+
+ RARFORMAT Format;
+ bool Solid;
+ bool Volume;
+ bool MainComment;
+ bool Locked;
+ bool Signed;
+ bool FirstVolume;
+ bool NewNumbering;
+ bool Protected;
+ bool Encrypted;
+ size_t SFXSize;
+ bool BrokenHeader;
+ bool FailedHeaderDecryption;
+
+#if !defined(SHELL_EXT) && !defined(RAR_NOCRYPT)
+ byte ArcSalt[SIZE_SALT50];
+#endif
+
+ bool Splitting;
+
+ uint VolNumber;
+ int64 VolWrite;
+ uint64 AddingFilesSize;
+ uint64 AddingHeadersSize;
+
+ bool NewArchive;
+
+ wchar FirstVolumeName[NM];
+};
+
+
+#endif
diff --git a/arcread.cpp b/arcread.cpp
new file mode 100644
index 0000000..03ea611
--- /dev/null
+++ b/arcread.cpp
@@ -0,0 +1,1412 @@
+#include "rar.hpp"
+
+size_t Archive::ReadHeader()
+{
+ // Once we failed to decrypt an encrypted block, there is no reason to
+ // attempt to do it further. We'll never be successful and only generate
+ // endless errors.
+ if (FailedHeaderDecryption)
+ return 0;
+
+ CurBlockPos=Tell();
+
+ size_t ReadSize;
+ switch(Format)
+ {
+#ifndef SFX_MODULE
+ case RARFMT14:
+ ReadSize=ReadHeader14();
+ break;
+#endif
+ case RARFMT15:
+ ReadSize=ReadHeader15();
+ break;
+ case RARFMT50:
+ ReadSize=ReadHeader50();
+ break;
+ }
+
+ if (ReadSize>0 && NextBlockPos<=CurBlockPos)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+ return ReadSize;
+}
+
+
+size_t Archive::SearchBlock(HEADER_TYPE HeaderType)
+{
+ size_t Size,Count=0;
+ while ((Size=ReadHeader())!=0 &&
+ (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC))
+ {
+ if ((++Count & 127)==0)
+ Wait();
+ if (GetHeaderType()==HeaderType)
+ return Size;
+ SeekToNext();
+ }
+ return 0;
+}
+
+
+size_t Archive::SearchSubBlock(const wchar *Type)
+{
+ size_t Size;
+ while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC)
+ {
+ if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type))
+ return Size;
+ SeekToNext();
+ }
+ return 0;
+}
+
+
+size_t Archive::SearchRR()
+{
+ // If locator extra field is available for recovery record, let's utilize it.
+ if (MainHead.Locator && MainHead.RROffset!=0)
+ {
+ uint64 CurPos=Tell();
+ Seek(MainHead.RROffset,SEEK_SET);
+ size_t Size=ReadHeader();
+ if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR))
+ return Size;
+ Seek(CurPos,SEEK_SET);
+ }
+ // Otherwise scan the entire archive to find the recovery record.
+ return SearchSubBlock(SUBHEAD_TYPE_RR);
+}
+
+
+void Archive::UnexpEndArcMsg()
+{
+ int64 ArcSize=FileLength();
+
+ // If block positions are equal to file size, this is not an error.
+ // It can happen when we reached the end of older RAR 1.5 archive,
+ // which did not have the end of archive block.
+ if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
+ {
+ uiMsg(UIERROR_UNEXPEOF,FileName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ }
+}
+
+
+void Archive::BrokenHeaderMsg()
+{
+ uiMsg(UIERROR_HEADERBROKEN,FileName);
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+}
+
+
+void Archive::UnkEncVerMsg(const wchar *Name)
+{
+ uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+}
+
+
+size_t Archive::ReadHeader15()
+{
+ RawRead Raw(this);
+
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3;
+
+ if (Decrypt)
+ {
+#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
+ return 0;
+#else
+ RequestArcPassword();
+
+ byte Salt[SIZE_SALT30];
+ if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL);
+ Raw.SetCrypt(&HeadersCrypt);
+#endif
+ }
+
+ Raw.Read(SIZEOF_SHORTBLOCKHEAD);
+ if (Raw.Size()==0)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ ShortBlock.HeadCRC=Raw.Get2();
+
+ ShortBlock.Reset();
+
+ uint HeaderType=Raw.Get1();
+ ShortBlock.Flags=Raw.Get2();
+ ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
+ ShortBlock.HeadSize=Raw.Get2();
+
+ ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
+ if (ShortBlock.HeadSizeReset();
+
+ *(BaseBlock *)hd=ShortBlock;
+
+ hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
+ hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
+ hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
+ hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
+ hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
+ hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
+ hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
+ hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
+ hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
+ hd->Version=(hd->Flags & LHD_VERSION)!=0;
+
+ hd->DataSize=Raw.Get4();
+ uint LowUnpSize=Raw.Get4();
+ hd->HostOS=Raw.Get1();
+
+ hd->FileHash.Type=HASH_CRC32;
+ hd->FileHash.CRC32=Raw.Get4();
+
+ uint FileTime=Raw.Get4();
+ hd->UnpVer=Raw.Get1();
+ hd->Method=Raw.Get1()-0x30;
+ size_t NameSize=Raw.Get2();
+ hd->FileAttr=Raw.Get4();
+
+ hd->CryptMethod=CRYPT_NONE;
+ if (hd->Encrypted)
+ switch(hd->UnpVer)
+ {
+ case 13: hd->CryptMethod=CRYPT_RAR13; break;
+ case 15: hd->CryptMethod=CRYPT_RAR15; break;
+ case 20:
+ case 26: hd->CryptMethod=CRYPT_RAR20; break;
+ default: hd->CryptMethod=CRYPT_RAR30; break;
+ }
+
+ hd->HSType=HSYS_UNKNOWN;
+ if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
+ hd->HSType=HSYS_UNIX;
+ else
+ if (hd->HostOSHSType=HSYS_WINDOWS;
+
+ hd->RedirType=FSREDIR_NONE;
+
+ // RAR 4.x Unix symlink.
+ if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
+ {
+ hd->RedirType=FSREDIR_UNIXSYMLINK;
+ *hd->RedirName=0;
+ }
+
+ hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
+
+ hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
+
+ uint HighPackSize,HighUnpSize;
+ if (hd->LargeFile)
+ {
+ HighPackSize=Raw.Get4();
+ HighUnpSize=Raw.Get4();
+ hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
+ }
+ else
+ {
+ HighPackSize=HighUnpSize=0;
+ // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
+ // that we do not know the unpacked file size and must unpack it
+ // until we find the end of file marker in compressed data.
+ hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
+ }
+ hd->PackSize=INT32TO64(HighPackSize,hd->DataSize);
+ hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize);
+ if (hd->UnknownUnpSize)
+ hd->UnpSize=INT64NDF;
+
+ char FileName[NM*4];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+
+ if (FileBlock)
+ {
+ if ((hd->Flags & LHD_UNICODE)!=0)
+ {
+ EncodeFileName NameCoder;
+ size_t Length=strlen(FileName);
+ Length++;
+ NameCoder.Decode(FileName,(byte *)FileName+Length,
+ NameSize-Length,hd->FileName,
+ ASIZE(hd->FileName));
+ }
+ else
+ *hd->FileName=0;
+
+ char AnsiName[NM];
+ IntToExt(FileName,AnsiName,ASIZE(AnsiName));
+ GetWideName(AnsiName,hd->FileName,hd->FileName,ASIZE(hd->FileName));
+
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
+#endif
+ ConvertFileHeader(hd);
+ }
+ else
+ {
+ CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
+
+ // Calculate the size of optional data.
+ int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
+ if ((hd->Flags & LHD_SALT)!=0)
+ DataSize-=SIZE_SALT30;
+
+ if (DataSize>0)
+ {
+ // Here we read optional additional fields for subheaders.
+ // They are stored after the file name and before salt.
+ hd->SubData.Alloc(DataSize);
+ Raw.GetB(&hd->SubData[0],DataSize);
+ if (hd->CmpName(SUBHEAD_TYPE_RR))
+ {
+ byte *D=&hd->SubData[8];
+ RecoverySize=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24);
+ RecoverySize*=512; // Sectors to size.
+ int64 CurPos=Tell();
+ RecoveryPercent=ToPercent(RecoverySize,CurPos);
+ // Round fractional percent exceeding .5 to upper value.
+ if (ToPercent(RecoverySize+CurPos/200,CurPos)>RecoveryPercent)
+ RecoveryPercent++;
+ }
+ }
+
+ if (hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+ }
+ if ((hd->Flags & LHD_SALT)!=0)
+ Raw.GetB(hd->Salt,SIZE_SALT30);
+ hd->mtime.SetDos(FileTime);
+ if ((hd->Flags & LHD_EXTTIME)!=0)
+ {
+ ushort Flags=Raw.Get2();
+ RarTime *tbl[4];
+ tbl[0]=&FileHead.mtime;
+ tbl[1]=&FileHead.ctime;
+ tbl[2]=&FileHead.atime;
+ tbl[3]=NULL; // Archive time is not used now.
+ for (int I=0;I<4;I++)
+ {
+ RarTime *CurTime=tbl[I];
+ uint rmode=Flags>>(3-I)*4;
+ if ((rmode & 8)==0 || CurTime==NULL)
+ continue;
+ if (I!=0)
+ {
+ uint DosTime=Raw.Get4();
+ CurTime->SetDos(DosTime);
+ }
+ RarLocalTime rlt;
+ CurTime->GetLocal(&rlt);
+ if (rmode & 4)
+ rlt.Second++;
+ rlt.Reminder=0;
+ int count=rmode&3;
+ for (int J=0;JSetLocal(&rlt);
+ }
+ }
+ NextBlockPos+=hd->PackSize;
+ bool CRCProcessedOnly=hd->CommentInHeader;
+ ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
+ if (hd->HeadCRC!=HeaderCRC)
+ {
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_WARNING);
+
+ // If we have a broken encrypted header, we do not need to display
+ // the error message here, because it will be displayed for such
+ // headers later in this function. Also such headers are unlikely
+ // to have anything sensible in file name field, so it is useless
+ // to display the file name.
+ if (!Decrypt)
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
+ }
+ }
+ break;
+ case HEAD_ENDARC:
+ *(BaseBlock *)&EndArcHead=ShortBlock;
+ EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
+ EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
+ EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
+ EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
+ if (EndArcHead.DataCRC)
+ EndArcHead.ArcDataCRC=Raw.Get4();
+ if (EndArcHead.StoreVolNumber)
+ VolNumber=EndArcHead.VolNumber=Raw.Get2();
+ break;
+#ifndef SFX_MODULE
+ case HEAD3_CMT:
+ *(BaseBlock *)&CommHead=ShortBlock;
+ CommHead.UnpSize=Raw.Get2();
+ CommHead.UnpVer=Raw.Get1();
+ CommHead.Method=Raw.Get1();
+ CommHead.CommCRC=Raw.Get2();
+ break;
+ case HEAD3_SIGN:
+ *(BaseBlock *)&SignHead=ShortBlock;
+ SignHead.CreationTime=Raw.Get4();
+ SignHead.ArcNameSize=Raw.Get2();
+ SignHead.UserNameSize=Raw.Get2();
+ break;
+ case HEAD3_AV:
+ *(BaseBlock *)&AVHead=ShortBlock;
+ AVHead.UnpVer=Raw.Get1();
+ AVHead.Method=Raw.Get1();
+ AVHead.AVVer=Raw.Get1();
+ AVHead.AVInfoCRC=Raw.Get4();
+ break;
+ case HEAD3_PROTECT:
+ *(BaseBlock *)&ProtectHead=ShortBlock;
+ ProtectHead.DataSize=Raw.Get4();
+ ProtectHead.Version=Raw.Get1();
+ ProtectHead.RecSectors=Raw.Get2();
+ ProtectHead.TotalBlocks=Raw.Get4();
+ Raw.GetB(ProtectHead.Mark,8);
+ NextBlockPos+=ProtectHead.DataSize;
+ RecoverySize=ProtectHead.RecSectors*512;
+ break;
+ case HEAD3_OLDSERVICE:
+ *(BaseBlock *)&SubBlockHead=ShortBlock;
+ SubBlockHead.DataSize=Raw.Get4();
+ NextBlockPos+=SubBlockHead.DataSize;
+ SubBlockHead.SubType=Raw.Get2();
+ SubBlockHead.Level=Raw.Get1();
+ switch(SubBlockHead.SubType)
+ {
+ case UO_HEAD:
+ *(SubBlockHeader *)&UOHead=SubBlockHead;
+ UOHead.OwnerNameSize=Raw.Get2();
+ UOHead.GroupNameSize=Raw.Get2();
+ if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
+ UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
+ if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
+ UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
+ Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
+ Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
+ UOHead.OwnerName[UOHead.OwnerNameSize]=0;
+ UOHead.GroupName[UOHead.GroupNameSize]=0;
+ break;
+ case MAC_HEAD:
+ *(SubBlockHeader *)&MACHead=SubBlockHead;
+ MACHead.fileType=Raw.Get4();
+ MACHead.fileCreator=Raw.Get4();
+ break;
+ case EA_HEAD:
+ case BEEA_HEAD:
+ case NTACL_HEAD:
+ *(SubBlockHeader *)&EAHead=SubBlockHead;
+ EAHead.UnpSize=Raw.Get4();
+ EAHead.UnpVer=Raw.Get1();
+ EAHead.Method=Raw.Get1();
+ EAHead.EACRC=Raw.Get4();
+ break;
+ case STREAM_HEAD:
+ *(SubBlockHeader *)&StreamHead=SubBlockHead;
+ StreamHead.UnpSize=Raw.Get4();
+ StreamHead.UnpVer=Raw.Get1();
+ StreamHead.Method=Raw.Get1();
+ StreamHead.StreamCRC=Raw.Get4();
+ StreamHead.StreamNameSize=Raw.Get2();
+ if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
+ StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
+ Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
+ StreamHead.StreamName[StreamHead.StreamNameSize]=0;
+ break;
+ }
+ break;
+#endif
+ default:
+ if (ShortBlock.Flags & LONG_BLOCK)
+ NextBlockPos+=Raw.Get4();
+ break;
+ }
+
+ ushort HeaderCRC=Raw.GetCRC15(false);
+
+ // Old AV header does not have header CRC properly set.
+ if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
+ ShortBlock.HeaderType!=HEAD3_AV)
+ {
+ bool Recovered=false;
+ if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
+ {
+ // Last 7 bytes of recovered volume can contain zeroes, because
+ // REV files store its own information (volume number, etc.) here.
+ SaveFilePos SavePos(*this);
+ int64 Length=Tell();
+ Seek(Length-7,SEEK_SET);
+ Recovered=true;
+ for (int J=0;J<7;J++)
+ if (GetByte()!=0)
+ Recovered=false;
+ }
+ if (!Recovered)
+ {
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+
+ if (Decrypt)
+ {
+ uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
+ FailedHeaderDecryption=true;
+ return 0;
+ }
+ }
+ }
+
+ if (NextBlockPos<=CurBlockPos)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+
+ return Raw.Size();
+}
+
+
+size_t Archive::ReadHeader50()
+{
+ RawRead Raw(this);
+
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5;
+
+ if (Decrypt)
+ {
+#if defined(SHELL_EXT) || defined(RAR_NOCRYPT)
+ return 0;
+#else
+ RequestArcPassword();
+
+ byte HeadersInitV[SIZE_INITV];
+ if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ byte PswCheck[SIZE_PSWCHECK];
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
+ // Verify password validity.
+ if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
+ {
+ uiMsg(UIERROR_BADPSW,FileName);
+ FailedHeaderDecryption=true;
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ return 0;
+ }
+
+ Raw.SetCrypt(&HeadersCrypt);
+#endif
+ }
+
+ // Header size must not occupy more than 3 variable length integer bytes
+ // resulting in 2 MB maximum header size, so here we read 4 byte CRC32
+ // followed by 3 bytes or less of header size.
+ const size_t FirstReadSize=7; // Smallest possible block size.
+ if (Raw.Read(FirstReadSize)=ShortBlock.HeadSize)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+ }
+
+ uint64 DataSize=0;
+ if ((ShortBlock.Flags & HFL_DATA)!=0)
+ DataSize=Raw.GetV();
+
+ NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize)+DataSize;
+
+ switch(ShortBlock.HeaderType)
+ {
+ case HEAD_CRYPT:
+ {
+ *(BaseBlock *)&CryptHead=ShortBlock;
+ uint CryptVersion=(uint)Raw.GetV();
+ if (CryptVersion>CRYPT_VERSION)
+ {
+ UnkEncVerMsg(FileName);
+ return 0;
+ }
+ uint EncFlags=(uint)Raw.GetV();
+ CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0;
+ CryptHead.Lg2Count=Raw.Get1();
+ if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ UnkEncVerMsg(FileName);
+ return 0;
+ }
+ Raw.GetB(CryptHead.Salt,SIZE_SALT50);
+ if (CryptHead.UsePswCheck)
+ {
+ Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK);
+
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+ }
+ Encrypted=true;
+ }
+ break;
+ case HEAD_MAIN:
+ {
+ MainHead.Reset();
+ *(BaseBlock *)&MainHead=ShortBlock;
+ uint ArcFlags=(uint)Raw.GetV();
+
+ Volume=(ArcFlags & MHFL_VOLUME)!=0;
+ Solid=(ArcFlags & MHFL_SOLID)!=0;
+ Locked=(ArcFlags & MHFL_LOCK)!=0;
+ Protected=(ArcFlags & MHFL_PROTECT)!=0;
+ Signed=false;
+ NewNumbering=true;
+
+ if ((ArcFlags & MHFL_VOLNUMBER)!=0)
+ VolNumber=(uint)Raw.GetV();
+ else
+ VolNumber=0;
+ FirstVolume=Volume && VolNumber==0;
+
+ if (ExtraSize!=0)
+ ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead);
+
+#ifdef USE_QOPEN
+ if (MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
+ {
+ // We seek to QO block in the end of archive when processing
+ // QOpen.Load, so we need to preserve current block positions
+ // to not break normal archive processing by calling function.
+ int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
+
+ QOpen.Init(this,false);
+ QOpen.Load(MainHead.QOpenOffset);
+
+ CurBlockPos=SaveCurBlockPos;
+ NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ }
+#endif
+ }
+ break;
+ case HEAD_FILE:
+ case HEAD_SERVICE:
+ {
+ FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
+ hd->Reset();
+ *(BaseBlock *)hd=ShortBlock;
+
+ bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
+
+ hd->LargeFile=true;
+
+ hd->PackSize=DataSize;
+ hd->FileFlags=(uint)Raw.GetV();
+ hd->UnpSize=Raw.GetV();
+
+ hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
+ if (hd->UnknownUnpSize)
+ hd->UnpSize=INT64NDF;
+
+ hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
+ hd->FileAttr=(uint)Raw.GetV();
+ if ((hd->FileFlags & FHFL_UTIME)!=0)
+ hd->mtime=(time_t)Raw.Get4();
+
+ hd->FileHash.Type=HASH_NONE;
+ if ((hd->FileFlags & FHFL_CRC32)!=0)
+ {
+ hd->FileHash.Type=HASH_CRC32;
+ hd->FileHash.CRC32=Raw.Get4();
+ }
+
+ hd->RedirType=FSREDIR_NONE;
+
+ uint CompInfo=(uint)Raw.GetV();
+ hd->Method=(CompInfo>>7) & 7;
+
+ // "+ 50" to not mix with old RAR format algorithms. For example,
+ // we may need to use the compression algorithm 15 in the future,
+ // but it was already used in RAR 1.5 and Unpack needs to distinguish
+ // them.
+ hd->UnpVer=(CompInfo & 0x3f) + 50;
+
+ hd->HostOS=(byte)Raw.GetV();
+ size_t NameSize=(size_t)Raw.GetV();
+ hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
+
+ hd->HSType=HSYS_UNKNOWN;
+ if (hd->HostOS==HOST5_UNIX)
+ hd->HSType=HSYS_UNIX;
+ else
+ if (hd->HostOS==HOST5_WINDOWS)
+ hd->HSType=HSYS_WINDOWS;
+
+ hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
+ hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
+ hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
+ hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
+ hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
+ hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
+
+ hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
+
+ char FileName[NM*4];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+
+ UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
+
+ // Should do it before converting names, because extra fields can
+ // affect name processing, like in case of NTFS streams.
+ if (ExtraSize!=0)
+ ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
+
+ if (FileBlock)
+ {
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
+#endif
+ ConvertFileHeader(hd);
+ }
+
+ if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+
+
+ if (BadCRC) // Add the file name to broken header message displayed above.
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
+ }
+ break;
+ case HEAD_ENDARC:
+ {
+ *(BaseBlock *)&EndArcHead=ShortBlock;
+ uint ArcFlags=(uint)Raw.GetV();
+ EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
+ EndArcHead.StoreVolNumber=false;
+ EndArcHead.DataCRC=false;
+ EndArcHead.RevSpace=false;
+ }
+ break;
+ }
+
+ if (NextBlockPos<=CurBlockPos)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+ return Raw.Size();
+}
+
+
+#if !defined(SHELL_EXT) && !defined(RAR_NOCRYPT)
+void Archive::RequestArcPassword()
+{
+ if (!Cmd->Password.IsSet())
+ {
+#ifdef RARDLL
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ }
+ if (!Cmd->Password.IsSet())
+ {
+ Close();
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#else
+ if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password) ||
+ !Cmd->Password.IsSet())
+ {
+ Close();
+ uiMsg(UIERROR_INCERRCOUNT);
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#endif
+ Cmd->ManualPassword=true;
+ }
+}
+#endif
+
+
+void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
+{
+ // Read extra data from the end of block skipping any fields before it.
+ size_t ExtraStart=Raw->Size()-ExtraSize;
+ if (ExtraStartGetPos())
+ return;
+ Raw->SetPos(ExtraStart);
+ while (Raw->DataLeft()>=2)
+ {
+ int64 FieldSize=Raw->GetV();
+ if (FieldSize==0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
+ break;
+ size_t NextPos=size_t(Raw->GetPos()+FieldSize);
+ uint64 FieldType=Raw->GetV();
+
+ FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields.
+
+ if (bb->HeaderType==HEAD_MAIN)
+ {
+ MainHeader *hd=(MainHeader *)bb;
+ if (FieldType==MHEXTRA_LOCATOR)
+ {
+ hd->Locator=true;
+ uint Flags=(uint)Raw->GetV();
+ if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->QOpenOffset=Offset+CurBlockPos;
+ }
+ if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->RROffset=Offset+CurBlockPos;
+ }
+ }
+ }
+
+ if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ switch(FieldType)
+ {
+ case FHEXTRA_CRYPT:
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ uint EncVersion=(uint)Raw->GetV();
+ if (EncVersion > CRYPT_VERSION)
+ UnkEncVerMsg(hd->FileName);
+ else
+ {
+ uint Flags=(uint)Raw->GetV();
+ hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
+ hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
+ hd->Lg2Count=Raw->Get1();
+ if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ UnkEncVerMsg(hd->FileName);
+ Raw->GetB(hd->Salt,SIZE_SALT50);
+ Raw->GetB(hd->InitV,SIZE_INITV);
+ if (hd->UsePswCheck)
+ {
+ Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
+
+ // It is important to know if password check data is valid.
+ // If it is damaged and header CRC32 fails to detect it,
+ // archiver would refuse to decompress a possibly valid file.
+ // Since we want to be sure distinguishing a wrong password
+ // or corrupt file data, we use 64-bit password check data
+ // and to control its validity we use 32 bits of password
+ // check data SHA-256 additionally to 32-bit header CRC32.
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+
+ // RAR 5.21 and earlier set PswCheck field in service records to 0
+ // even if UsePswCheck was present.
+ if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
+ hd->UsePswCheck=0;
+ }
+ hd->SaltSet=true;
+ hd->CryptMethod=CRYPT_RAR50;
+ hd->Encrypted=true;
+ }
+ }
+ break;
+ case FHEXTRA_HASH:
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ uint Type=(uint)Raw->GetV();
+ if (Type==FHEXTRA_HASH_BLAKE2)
+ {
+ hd->FileHash.Type=HASH_BLAKE2;
+ Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
+ }
+ }
+ break;
+ case FHEXTRA_HTIME:
+ if (FieldSize>=9)
+ {
+ byte Flags=(byte)Raw->GetV();
+ bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
+ if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
+ if (UnixTime)
+ hd->mtime=(time_t)Raw->Get4();
+ else
+ hd->mtime.SetRaw(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
+ if (UnixTime)
+ hd->ctime=(time_t)Raw->Get4();
+ else
+ hd->ctime.SetRaw(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
+ if (UnixTime)
+ hd->atime=(time_t)Raw->Get4();
+ else
+ hd->atime.SetRaw(Raw->Get8());
+ }
+ break;
+ case FHEXTRA_VERSION:
+ if (FieldSize>=1)
+ {
+ Raw->GetV(); // Skip flags field.
+ uint Version=(uint)Raw->GetV();
+ if (Version!=0)
+ {
+ hd->Version=true;
+
+ wchar VerText[20];
+ swprintf(VerText,ASIZE(VerText),L";%u",Version);
+ wcsncatz(FileHead.FileName,VerText,ASIZE(FileHead.FileName));
+ }
+ }
+ break;
+ case FHEXTRA_REDIR:
+ {
+ hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
+ uint Flags=(uint)Raw->GetV();
+ hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
+ size_t NameSize=(size_t)Raw->GetV();
+
+ char UtfName[NM*4];
+ *UtfName=0;
+ if (NameSizeGetB(UtfName,NameSize);
+ UtfName[NameSize]=0;
+ }
+#ifdef _WIN_ALL
+ UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
+#endif
+ UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
+ }
+ break;
+ case FHEXTRA_UOWNER:
+ {
+ uint Flags=(uint)Raw->GetV();
+ hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
+ hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
+ *hd->UnixOwnerName=*hd->UnixGroupName=0;
+ if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
+ {
+ size_t Length=(size_t)Raw->GetV();
+ Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
+ Raw->GetB(hd->UnixOwnerName,Length);
+ hd->UnixOwnerName[Length]=0;
+ }
+ if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
+ {
+ size_t Length=(size_t)Raw->GetV();
+ Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
+ Raw->GetB(hd->UnixGroupName,Length);
+ hd->UnixGroupName[Length]=0;
+ }
+#ifdef _UNIX
+ if (hd->UnixOwnerNumeric)
+ hd->UnixOwnerID=(uid_t)Raw->GetV();
+ if (hd->UnixGroupNumeric)
+ hd->UnixGroupID=(gid_t)Raw->GetV();
+#else
+ // Need these fields in Windows too for 'list' command,
+ // but uid_t and gid_t are not defined.
+ if (hd->UnixOwnerNumeric)
+ hd->UnixOwnerID=(uint)Raw->GetV();
+ if (hd->UnixGroupNumeric)
+ hd->UnixGroupID=(uint)Raw->GetV();
+#endif
+ hd->UnixOwnerSet=true;
+ }
+ break;
+ case FHEXTRA_SUBDATA:
+ {
+ // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than
+ // required. It did not hurt extraction, because UnRAR 5.21
+ // and earlier ignored this field and set FieldSize as data left
+ // in entire extra area. But now we set the correct field size
+ // and set FieldSize based on actual extra record size,
+ // so we need to adjust it for those older archives here.
+ // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE
+ // and always is last in extra area. So since its size is by 1
+ // less than needed, we always have 1 byte left in extra area,
+ // which fact we use here to detect such archives.
+ if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1)
+ FieldSize++;
+
+ hd->SubData.Alloc((size_t)FieldSize);
+ Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
+ }
+ break;
+ }
+ }
+
+ Raw->SetPos(NextPos);
+ }
+}
+
+
+#ifndef SFX_MODULE
+size_t Archive::ReadHeader14()
+{
+ RawRead Raw(this);
+ if (CurBlockPos<=(int64)SFXSize)
+ {
+ Raw.Read(SIZEOF_MAINHEAD14);
+ MainHead.Reset();
+ byte Mark[4];
+ Raw.GetB(Mark,4);
+ uint HeadSize=Raw.Get2();
+ byte Flags=Raw.Get1();
+ NextBlockPos=CurBlockPos+HeadSize;
+ CurHeaderType=HEAD_MAIN;
+
+ Volume=(Flags & MHD_VOLUME)!=0;
+ Solid=(Flags & MHD_SOLID)!=0;
+ Locked=(Flags & MHD_LOCK)!=0;
+ MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
+ MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
+ }
+ else
+ {
+ Raw.Read(SIZEOF_FILEHEAD14);
+ FileHead.Reset();
+
+ FileHead.HeaderType=HEAD_FILE;
+ FileHead.DataSize=Raw.Get4();
+ FileHead.UnpSize=Raw.Get4();
+ FileHead.FileHash.Type=HASH_RAR14;
+ FileHead.FileHash.CRC32=Raw.Get2();
+ FileHead.HeadSize=Raw.Get2();
+ uint FileTime=Raw.Get4();
+ FileHead.FileAttr=Raw.Get1();
+ FileHead.Flags=Raw.Get1()|LONG_BLOCK;
+ FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
+ size_t NameSize=Raw.Get1();
+ FileHead.Method=Raw.Get1();
+
+ FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
+ FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
+ FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
+ FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE;
+
+ FileHead.PackSize=FileHead.DataSize;
+ FileHead.WinSize=0x10000;
+
+ FileHead.mtime.SetDos(FileTime);
+
+ Raw.Read(NameSize);
+
+ char FileName[NM];
+ Raw.GetB((byte *)FileName,Min(NameSize,ASIZE(FileName)));
+ FileName[NameSize]=0;
+ IntToExt(FileName,FileName,ASIZE(FileName));
+ CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
+ ConvertNameCase(FileHead.FileName);
+
+ if (Raw.Size()!=0)
+ NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
+ CurHeaderType=HEAD_FILE;
+ }
+ return NextBlockPos>CurBlockPos ? Raw.Size() : 0;
+}
+#endif
+
+
+#ifndef SFX_MODULE
+void Archive::ConvertNameCase(wchar *Name)
+{
+ if (Cmd->ConvertNames==NAMES_UPPERCASE)
+ wcsupper(Name);
+ if (Cmd->ConvertNames==NAMES_LOWERCASE)
+ wcslower(Name);
+}
+#endif
+
+
+bool Archive::IsArcDir()
+{
+ return FileHead.Dir;
+}
+
+
+void Archive::ConvertAttributes()
+{
+#if defined(_WIN_ALL) || defined(_EMX)
+ if (FileHead.HSType!=HSYS_WINDOWS)
+ FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
+#endif
+#ifdef _UNIX
+ // umask defines which permission bits must not be set by default
+ // when creating a file or directory. The typical default value
+ // for the process umask is S_IWGRP | S_IWOTH (octal 022),
+ // resulting in 0644 mode for new files.
+ // Normally umask is applied automatically when creating a file,
+ // but we set attributes with chmod later, so we need to calculate
+ // resulting attributes here. We do it only for non-Unix archives.
+ // We restore native Unix attributes as is, because it can be backup.
+ static mode_t mask = (mode_t) -1;
+
+ if (mask == (mode_t) -1)
+ {
+ // umask call returns the current umask value. Argument (022) is not
+ // really important here.
+ mask = umask(022);
+
+ // Restore the original umask value, which was changed to 022 above.
+ umask(mask);
+ }
+
+ switch(FileHead.HSType)
+ {
+ case HSYS_WINDOWS:
+ {
+ // Mapping MSDOS, OS/2 and Windows file attributes to Unix.
+
+ if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
+ {
+ // For directories we use 0777 mask.
+ FileHead.FileAttr=0777 & ~mask;
+ }
+ else
+ if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
+ {
+ // For read only files we use 0444 mask with 'w' bits turned off.
+ FileHead.FileAttr=0444 & ~mask;
+ }
+ else
+ {
+ // umask does not set +x for regular files, so we use 0666
+ // instead of 0777 as for directories.
+ FileHead.FileAttr=0666 & ~mask;
+ }
+ }
+ break;
+ case HSYS_UNIX:
+ break;
+ default:
+ if (FileHead.Dir)
+ FileHead.FileAttr=0x41ff & ~mask;
+ else
+ FileHead.FileAttr=0x81b6 & ~mask;
+ break;
+ }
+#endif
+}
+
+
+void Archive::ConvertFileHeader(FileHeader *hd)
+{
+ if (Format==RARFMT15 && hd->UnpVer<20 && (hd->FileAttr & 0x10))
+ hd->Dir=true;
+ if (hd->HSType==HSYS_UNKNOWN)
+ if (hd->Dir)
+ hd->FileAttr=0x10;
+ else
+ hd->FileAttr=0x20;
+
+#ifdef _WIN_ALL
+ if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
+ ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
+#endif
+
+ for (wchar *s=hd->FileName;*s!=0;s++)
+ {
+#ifdef _UNIX
+ // Backslash is the invalid character for Windows file headers,
+ // but it can present in Unix file names extracted in Unix.
+ if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS)
+ *s='_';
+#endif
+
+#if defined(_WIN_ALL) || defined(_EMX)
+ // RAR 5.0 archives do not use '\' as path separator, so if we see it,
+ // it means that it is a part of Unix file name, which we cannot
+ // extract in Windows.
+ if (*s=='\\' && Format==RARFMT50)
+ *s='_';
+
+ // ':' in file names is allowed in Unix, but not in Windows.
+ // Even worse, file data will be written to NTFS stream on NTFS,
+ // so automatic name correction on file create error in extraction
+ // routine does not work. In Windows and DOS versions we better
+ // replace ':' now.
+ if (*s==':')
+ *s='_';
+#endif
+
+ // This code must be performed only after other path separator checks,
+ // because it produces backslashes illegal for some of checks above.
+ // Backslash is allowed in file names in Unix, but not in Windows.
+ // Still, RAR 4.x uses backslashes as path separator even in Unix.
+ // Forward slash is not allowed in both systems. In RAR 5.0 we use
+ // the forward slash as universal path separator.
+ if (*s=='/' || *s=='\\' && Format!=RARFMT50)
+ *s=CPATHDIVIDER;
+ }
+}
+
+
+int64 Archive::GetStartPos()
+{
+ int64 StartPos=SFXSize+MarkHead.HeadSize;
+ if (Format==RARFMT15)
+ StartPos+=MainHead.HeadSize;
+ else // RAR 5.0.
+ StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize);
+ return StartPos;
+}
+
+
+#ifndef SHELL_EXT
+bool Archive::ReadSubData(Array *UnpData,File *DestFile)
+{
+ if (BrokenHeader)
+ {
+ uiMsg(UIERROR_SUBHEADERBROKEN,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ return false;
+ }
+ if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
+ {
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
+ }
+
+ if (SubHead.PackSize==0 && !SubHead.SplitAfter)
+ return true;
+
+ SubDataIO.Init();
+ Unpack Unpack(&SubDataIO);
+ Unpack.Init(SubHead.WinSize,false);
+
+ if (DestFile==NULL)
+ {
+ if (SubHead.UnpSize>0x1000000)
+ {
+ // So huge allocation must never happen in valid archives.
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
+ }
+ if (UnpData==NULL)
+ SubDataIO.SetTestMode(true);
+ else
+ {
+ UnpData->Alloc((size_t)SubHead.UnpSize);
+ SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
+ }
+ }
+ if (SubHead.Encrypted)
+ if (Cmd->Password.IsSet())
+ SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password,
+ SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV,
+ SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck);
+ else
+ return false;
+ SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1);
+ SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
+ SubDataIO.EnableShowProgress(false);
+ SubDataIO.SetFiles(this,DestFile);
+ SubDataIO.UnpVolume=SubHead.SplitAfter;
+ SubDataIO.SetSubHeader(&SubHead,NULL);
+ Unpack.SetDestSize(SubHead.UnpSize);
+ if (SubHead.Method==0)
+ CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
+ else
+ Unpack.DoUnpack(SubHead.UnpVer,false);
+
+ if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL))
+ {
+ uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ if (UnpData!=NULL)
+ UnpData->Reset();
+ return false;
+ }
+ return true;
+}
+#endif
diff --git a/array.hpp b/array.hpp
new file mode 100644
index 0000000..63d89c3
--- /dev/null
+++ b/array.hpp
@@ -0,0 +1,165 @@
+#ifndef _RAR_ARRAY_
+#define _RAR_ARRAY_
+
+extern ErrorHandler ErrHandler;
+
+template class Array
+{
+ private:
+ T *Buffer;
+ size_t BufSize;
+ size_t AllocSize;
+ size_t MaxSize;
+ public:
+ Array();
+ Array(size_t Size);
+ Array(const Array &Src); // Copy constructor.
+ ~Array();
+ inline void CleanData();
+ inline T& operator [](size_t Item) const;
+ inline T* operator + (size_t Pos);
+ inline size_t Size(); // Returns the size in items, not in bytes.
+ void Add(size_t Items);
+ void Alloc(size_t Items);
+ void Reset();
+ void SoftReset();
+ void operator = (Array &Src);
+ void Push(T Item);
+ void Append(T *Item,size_t Count);
+ T* Addr(size_t Item) {return Buffer+Item;}
+ void SetMaxSize(size_t Size) {MaxSize=Size;}
+};
+
+template void Array::CleanData()
+{
+ Buffer=NULL;
+ BufSize=0;
+ AllocSize=0;
+ MaxSize=0;
+}
+
+
+template Array::Array()
+{
+ CleanData();
+}
+
+
+template Array::Array(size_t Size)
+{
+ CleanData();
+ Add(Size);
+}
+
+
+// Copy constructor in case we need to pass an object as value.
+template Array::Array(const Array &Src)
+{
+ CleanData();
+ Alloc(Src.BufSize);
+ if (Src.BufSize!=0)
+ memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
+}
+
+
+template Array::~Array()
+{
+ if (Buffer!=NULL)
+ free(Buffer);
+}
+
+
+template inline T& Array::operator [](size_t Item) const
+{
+ return Buffer[Item];
+}
+
+
+template inline T* Array::operator +(size_t Pos)
+{
+ return Buffer+Pos;
+}
+
+
+template inline size_t Array::Size()
+{
+ return BufSize;
+}
+
+
+template void Array::Add(size_t Items)
+{
+ BufSize+=Items;
+ if (BufSize>AllocSize)
+ {
+ if (MaxSize!=0 && BufSize>MaxSize)
+ {
+ ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
+ ErrHandler.MemoryError();
+ }
+
+ size_t Suggested=AllocSize+AllocSize/4+32;
+ size_t NewSize=Max(BufSize,Suggested);
+
+ T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
+ if (NewBuffer==NULL)
+ ErrHandler.MemoryError();
+ Buffer=NewBuffer;
+ AllocSize=NewSize;
+ }
+}
+
+
+template void Array::Alloc(size_t Items)
+{
+ if (Items>AllocSize)
+ Add(Items-BufSize);
+ else
+ BufSize=Items;
+}
+
+
+template void Array::Reset()
+{
+ if (Buffer!=NULL)
+ {
+ free(Buffer);
+ Buffer=NULL;
+ }
+ BufSize=0;
+ AllocSize=0;
+}
+
+
+// Reset buffer size, but preserve already allocated memory if any,
+// so we can reuse it without wasting time to allocation.
+template void Array::SoftReset()
+{
+ BufSize=0;
+}
+
+
+template void Array::operator =(Array &Src)
+{
+ Reset();
+ Alloc(Src.BufSize);
+ if (Src.BufSize!=0)
+ memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
+}
+
+
+template void Array::Push(T Item)
+{
+ Add(1);
+ (*this)[Size()-1]=Item;
+}
+
+
+template void Array::Append(T *Items,size_t Count)
+{
+ size_t CurSize=Size();
+ Add(Count);
+ memcpy(Buffer+CurSize,Items,Count*sizeof(T));
+}
+
+#endif
diff --git a/blake2s.cpp b/blake2s.cpp
new file mode 100644
index 0000000..d481bc6
--- /dev/null
+++ b/blake2s.cpp
@@ -0,0 +1,189 @@
+// Based on public domain code written in 2012 by Samuel Neves
+
+#include "rar.hpp"
+
+#ifdef USE_SSE
+#include "blake2s_sse.cpp"
+#endif
+
+static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
+static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
+static void blake2s_final( blake2s_state *S, byte *digest );
+
+#include "blake2sp.cpp"
+
+static const uint32 blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const byte blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+static inline void blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+}
+
+
+/* Some helper functions, not necessarily useful */
+static inline void blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+}
+
+
+static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+}
+
+
+/* init2 xors IV with input parameter block */
+void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
+{
+#ifdef USE_SSE
+ if (_SSE_Version>=SSE_SSE2)
+ blake2s_init_sse();
+#endif
+
+ S->init(); // Clean data.
+ for( int i = 0; i < 8; ++i )
+ S->h[i] = blake2s_IV[i];
+
+ S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
+ S->h[2] ^= node_offset;
+ S->h[3] ^= (node_depth<<16)|0x20000000;
+}
+
+
+static _forceinline uint32 rotr32( const uint32 w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+
+#define G(r,i,m,a,b,c,d) \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7);
+
+
+static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
+{
+ uint32 m[16];
+ uint32 v[16];
+
+ for( size_t i = 0; i < 16; ++i )
+ m[i] = RawGet4( block + i * 4 );
+
+ for( size_t i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2s_IV[0];
+ v[ 9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+
+ for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
+ {
+ G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
+ G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
+ G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
+ G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
+ G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
+ G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
+ G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
+ G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
+ }
+
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+}
+
+
+void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ size_t left = S->buflen;
+ size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+
+#ifdef USE_SSE
+#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
+ if (_SSE_Version>=SSE_SSE2)
+#else
+ if (_SSE_Version>=SSE_SSSE3)
+#endif
+ blake2s_compress_sse( S, S->buf );
+ else
+ blake2s_compress( S, S->buf ); // Compress
+#else
+ blake2s_compress( S, S->buf ); // Compress
+#endif
+
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, (size_t)inlen );
+ S->buflen += (size_t)inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+}
+
+
+void blake2s_final( blake2s_state *S, byte *digest )
+{
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32 )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( int i = 0; i < 8; ++i ) /* Output full hash */
+ RawPut4( S->h[i], digest + 4 * i );
+}
+
diff --git a/blake2s.hpp b/blake2s.hpp
new file mode 100644
index 0000000..7dd7157
--- /dev/null
+++ b/blake2s.hpp
@@ -0,0 +1,101 @@
+// Based on public domain code written in 2012 by Samuel Neves
+#ifndef _RAR_BLAKE2_
+#define _RAR_BLAKE2_
+
+#define BLAKE2_DIGEST_SIZE 32
+
+enum blake2s_constant
+{
+ BLAKE2S_BLOCKBYTES = 64,
+ BLAKE2S_OUTBYTES = 32
+};
+
+
+// Alignment to 64 improves performance of both SSE and non-SSE versions.
+// Alignment to n*16 is required for SSE version, so we selected 64.
+// We use the custom alignment scheme instead of __declspec(align(x)),
+// because it is less compiler dependent. Also the compiler directive
+// does not help if structure is a member of class allocated through
+// 'new' operator.
+struct blake2s_state
+{
+ enum { BLAKE_ALIGNMENT = 64 };
+
+ // buffer and uint32 h[8], t[2], f[2];
+ enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
+
+ byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
+
+ byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
+ uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
+
+ size_t buflen;
+ byte last_node;
+
+ blake2s_state()
+ {
+ set_pointers();
+ }
+
+ // Required when we declare and assign in the same command.
+ blake2s_state(blake2s_state &st)
+ {
+ set_pointers();
+ *this=st;
+ }
+
+ void set_pointers()
+ {
+ // Set aligned pointers. Must be done in constructor, not in Init(),
+ // so assignments like 'blake2sp_state res=blake2ctx' work correctly
+ // even if blake2sp_init is not called for 'res'.
+ buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
+ h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
+ t = h + 8;
+ f = t + 2;
+ }
+
+ void init()
+ {
+ memset( ubuf, 0, sizeof( ubuf ) );
+ buflen = 0;
+ last_node = 0;
+ }
+
+ // Since we use pointers, the default = would work incorrectly.
+ blake2s_state& operator = (blake2s_state &st)
+ {
+ if (this != &st)
+ {
+ memcpy(buf, st.buf, BLAKE_DATA_SIZE);
+ buflen = st.buflen;
+ last_node = st.last_node;
+ }
+ return *this;
+ }
+};
+
+
+#ifdef RAR_SMP
+class ThreadPool;
+#endif
+
+struct blake2sp_state
+{
+ blake2s_state S[8];
+ blake2s_state R;
+ byte buf[8 * BLAKE2S_BLOCKBYTES];
+ size_t buflen;
+
+#ifdef RAR_SMP
+ ThreadPool *ThPool;
+ uint MaxThreads;
+#endif
+};
+
+void blake2sp_init( blake2sp_state *S );
+void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
+void blake2sp_final( blake2sp_state *S, byte *digest );
+
+#endif
+
diff --git a/blake2s_sse.cpp b/blake2s_sse.cpp
new file mode 100644
index 0000000..af34b01
--- /dev/null
+++ b/blake2s_sse.cpp
@@ -0,0 +1,127 @@
+// Based on public domain code written in 2012 by Samuel Neves
+
+extern const byte blake2s_sigma[10][16];
+
+// Initialization vector.
+static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
+
+#ifdef _WIN_64
+// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
+static __m128i crotr8, crotr16;
+#endif
+
+static void blake2s_init_sse()
+{
+ // We cannot initialize these 128 bit variables in place when declaring
+ // them globally, because global scope initialization is performed before
+ // our SSE check and it would make code incompatible with older non-SSE2
+ // CPUs. Also we cannot initialize them as static inside of function
+ // using these variables, because SSE static initialization is not thread
+ // safe: first thread starts initialization and sets "init done" flag even
+ // if it is not done yet, second thread can attempt to access half-init
+ // SSE data. So we moved init code here.
+
+ blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
+ blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
+
+#ifdef _WIN_64
+ crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
+ crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
+#endif
+}
+
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#ifdef _WIN_32
+// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
+// to not use _mm_shuffle_epi8 here.
+#define mm_rotr_epi32(r, c) ( \
+ _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
+#else
+#define mm_rotr_epi32(r, c) ( \
+ c==8 ? _mm_shuffle_epi8(r,crotr8) \
+ : c==16 ? _mm_shuffle_epi8(r,crotr16) \
+ : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
+#endif
+
+
+#define G1(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = mm_rotr_epi32(row4, 16); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = mm_rotr_epi32(row2, 12);
+
+#define G2(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = mm_rotr_epi32(row4, 8); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = mm_rotr_epi32(row2, 7);
+
+#define DIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
+
+#define UNDIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
+
+#ifdef _WIN_64
+ // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
+ // from stack operations, which are slower than this code.
+ #define _mm_set_epi32(i3,i2,i1,i0) \
+ _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
+ _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
+#endif
+
+// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
+// and about the same in x64 mode in our test. Perhaps depends on compiler.
+#define SSE_ROUND(m,row,r) \
+{ \
+ __m128i buf; \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
+ G1(row[0],row[1],row[2],row[3],buf); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
+ G2(row[0],row[1],row[2],row[3],buf); \
+ DIAGONALIZE(row[0],row[1],row[2],row[3]); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
+ G1(row[0],row[1],row[2],row[3],buf); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
+ G2(row[0],row[1],row[2],row[3],buf); \
+ UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
+}
+
+
+static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
+{
+ __m128i row[4];
+ __m128i ff0, ff1;
+
+ const uint32 *m = ( uint32 * )block;
+
+ row[0] = ff0 = LOAD( &S->h[0] );
+ row[1] = ff1 = LOAD( &S->h[4] );
+
+ row[2] = blake2s_IV_0_3;
+ row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
+ SSE_ROUND( m, row, 0 );
+ SSE_ROUND( m, row, 1 );
+ SSE_ROUND( m, row, 2 );
+ SSE_ROUND( m, row, 3 );
+ SSE_ROUND( m, row, 4 );
+ SSE_ROUND( m, row, 5 );
+ SSE_ROUND( m, row, 6 );
+ SSE_ROUND( m, row, 7 );
+ SSE_ROUND( m, row, 8 );
+ SSE_ROUND( m, row, 9 );
+ STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
+ STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
+ return 0;
+}
diff --git a/blake2sp.cpp b/blake2sp.cpp
new file mode 100644
index 0000000..da64588
--- /dev/null
+++ b/blake2sp.cpp
@@ -0,0 +1,153 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see .
+*/
+
+#define PARALLELISM_DEGREE 8
+
+void blake2sp_init( blake2sp_state *S )
+{
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ blake2s_init_param( &S->R, 0, 1 ); // Init root.
+
+ for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
+
+ S->R.last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1].last_node = 1;
+}
+
+
+struct Blake2ThreadData
+{
+ void Update();
+ blake2s_state *S;
+ const byte *in;
+ size_t inlen;
+};
+
+
+void Blake2ThreadData::Update()
+{
+ size_t inlen__ = inlen;
+ const byte *in__ = ( const byte * )in;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+#ifdef USE_SSE
+ // We gain 5% in i7 SSE mode by prefetching next data block.
+ if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
+ _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
+#endif
+ blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+}
+
+#ifdef RAR_SMP
+THREAD_PROC(Blake2Thread)
+{
+ Blake2ThreadData *td=(Blake2ThreadData *)Data;
+ td->Update();
+}
+#endif
+
+
+void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+ Blake2ThreadData btd_array[PARALLELISM_DEGREE];
+
+#ifdef RAR_SMP
+ uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
+
+ if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
+ ThreadNumber=4;
+#else
+ uint ThreadNumber=1;
+#endif
+
+ for (size_t id__=0;id__inlen = inlen;
+ btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
+ btd->S = &S->S[id__];
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ S->ThPool->AddTask(Blake2Thread,(void*)btd);
+ else
+ btd->Update();
+#else
+ btd->Update();
+#endif
+ id__++;
+ }
+#ifdef RAR_SMP
+ if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
+ S->ThPool->WaitDone();
+#endif // RAR_SMP
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, (size_t)inlen );
+
+ S->buflen = left + (size_t)inlen;
+}
+
+
+void blake2sp_final( blake2sp_state *S, byte *digest )
+{
+ byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+ if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+ blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+ }
+
+ blake2s_final( &S->S[i], hash[i] );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
+
+ blake2s_final( &S->R, digest );
+}
diff --git a/cmddata.cpp b/cmddata.cpp
new file mode 100644
index 0000000..02c4f2f
--- /dev/null
+++ b/cmddata.cpp
@@ -0,0 +1,1374 @@
+#include "rar.hpp"
+
+CommandData::CommandData()
+{
+ Init();
+}
+
+
+void CommandData::Init()
+{
+ RAROptions::Init();
+
+ *Command=0;
+ *ArcName=0;
+ FileLists=false;
+
+ ListMode=RCLM_AUTO;
+
+ BareOutput=false;
+
+
+ FileArgs.Reset();
+ ExclArgs.Reset();
+ InclArgs.Reset();
+ StoreArgs.Reset();
+ ArcNames.Reset();
+ NextVolSizes.Reset();
+}
+
+
+// Return the pointer to next position in the string and store dynamically
+// allocated command line parameter in Par.
+static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
+{
+ const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
+ if (NextCmd==NULL)
+ return NULL;
+ size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
+ *Par=(wchar *)malloc(ParSize*sizeof(wchar));
+ if (*Par==NULL)
+ return NULL;
+ return GetCmdParam(CmdLine,*Par,ParSize);
+}
+
+
+#if !defined(SFX_MODULE) && !defined(_ANDROID)
+void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
+{
+ *Command=0;
+ NoMoreSwitches=false;
+#ifdef CUSTOM_CMDLINE_PARSER
+ // In Windows we may prefer to implement our own command line parser
+ // to avoid replacing \" by " in standard parser. Such replacing corrupts
+ // destination paths like "dest path\" in extraction commands.
+ // Also our own parser is Unicode compatible.
+ const wchar *CmdLine=GetCommandLine();
+
+ wchar *Par;
+ for (bool FirstParam=true;;FirstParam=false)
+ {
+ if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
+ break;
+ if (!FirstParam) // First parameter is the executable name.
+ if (Preprocess)
+ PreprocessArg(Par);
+ else
+ ParseArg(Par);
+ free(Par);
+ }
+#else
+ Array Arg;
+ for (int I=1;I EnvStrW(strlen(EnvStr)+1);
+ CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
+ ProcessSwitchesString(&EnvStrW[0]);
+ }
+}
+#endif
+
+
+
+#if !defined(SFX_MODULE) && !defined(_ANDROID)
+// Preprocess those parameters, which must be processed before the rest of
+// command line. Return 'false' to stop further processing.
+void CommandData::PreprocessArg(const wchar *Arg)
+{
+ if (IsSwitch(Arg[0]) && !NoMoreSwitches)
+ {
+ Arg++;
+ if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
+ NoMoreSwitches=true;
+ if (wcsicomp(Arg,L"cfg-")==0)
+ ConfigDisabled=true;
+#ifndef GUI
+ if (wcsnicomp(Arg,L"ilog",4)==0)
+ {
+ // Ensure that correct log file name is already set
+ // if we need to report an error when processing the command line.
+ ProcessSwitch(Arg);
+ InitLogOptions(LogName,ErrlogCharset);
+ }
+#endif
+ if (wcsnicomp(Arg,L"sc",2)==0)
+ {
+ // Process -sc before reading any file lists.
+ ProcessSwitch(Arg);
+#ifndef GUI
+ if (*LogName!=0)
+ InitLogOptions(LogName,ErrlogCharset);
+#endif
+ }
+ }
+ else
+ if (*Command==0)
+ wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
+}
+#endif
+
+
+#if !defined(GUI) && !defined(SFX_MODULE) && !defined(_ANDROID)
+void CommandData::ReadConfig()
+{
+ StringList List;
+ if (ReadTextFile(DefConfigName,&List,true))
+ {
+ wchar *Str;
+ while ((Str=List.GetString())!=NULL)
+ {
+ while (IsSpace(*Str))
+ Str++;
+ if (wcsnicomp(Str,L"switches=",9)==0)
+ ProcessSwitchesString(Str+9);
+ if (*Command!=0)
+ {
+ wchar Cmd[16];
+ wcsncpyz(Cmd,Command,ASIZE(Cmd));
+ wchar C0=toupperw(Cmd[0]);
+ wchar C1=toupperw(Cmd[1]);
+ if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
+ Cmd[1]=0;
+ if (C0=='R' && (C1=='R' || C1=='V'))
+ Cmd[2]=0;
+ wchar SwName[16+ASIZE(Cmd)];
+ swprintf(SwName,ASIZE(SwName),L"switches_%s=",Cmd);
+ size_t Length=wcslen(SwName);
+ if (wcsnicomp(Str,SwName,Length)==0)
+ ProcessSwitchesString(Str+Length);
+ }
+ }
+ }
+}
+#endif
+
+
+#if !defined(SFX_MODULE) && !defined(_ANDROID)
+void CommandData::ProcessSwitchesString(const wchar *Str)
+{
+ wchar *Par;
+ while ((Str=AllocCmdParam(Str,&Par))!=NULL)
+ {
+ if (IsSwitch(*Par))
+ ProcessSwitch(Par+1);
+ free(Par);
+ }
+}
+#endif
+
+
+#if !defined(SFX_MODULE) && !defined(_ANDROID)
+void CommandData::ProcessSwitch(const wchar *Switch)
+{
+
+ switch(toupperw(Switch[0]))
+ {
+ case '@':
+ ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS;
+ break;
+ case 'A':
+ switch(toupperw(Switch[1]))
+ {
+ case 'C':
+ ClearArc=true;
+ break;
+ case 'D':
+ AppendArcNameToPath=true;
+ break;
+#ifndef SFX_MODULE
+ case 'G':
+ if (Switch[2]=='-' && Switch[3]==0)
+ GenerateArcName=0;
+ else
+ {
+ GenerateArcName=true;
+ wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
+ }
+ break;
+#endif
+ case 'I':
+ IgnoreGeneralAttr=true;
+ break;
+ case 'N': // Reserved for archive name.
+ break;
+ case 'O':
+ AddArcOnly=true;
+ break;
+ case 'P':
+ wcscpy(ArcPath,Switch+2);
+ break;
+ case 'S':
+ SyncFiles=true;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'C':
+ if (Switch[2]==0)
+ switch(toupperw(Switch[1]))
+ {
+ case '-':
+ DisableComment=true;
+ break;
+ case 'U':
+ ConvertNames=NAMES_UPPERCASE;
+ break;
+ case 'L':
+ ConvertNames=NAMES_LOWERCASE;
+ break;
+ }
+ break;
+ case 'D':
+ if (Switch[2]==0)
+ switch(toupperw(Switch[1]))
+ {
+ case 'S':
+ DisableSortSolid=true;
+ break;
+ case 'H':
+ OpenShared=true;
+ break;
+ case 'F':
+ DeleteFiles=true;
+ break;
+ }
+ break;
+ case 'E':
+ switch(toupperw(Switch[1]))
+ {
+ case 'P':
+ switch(Switch[2])
+ {
+ case 0:
+ ExclPath=EXCL_SKIPWHOLEPATH;
+ break;
+ case '1':
+ ExclPath=EXCL_BASEPATH;
+ break;
+ case '2':
+ ExclPath=EXCL_SAVEFULLPATH;
+ break;
+ case '3':
+ ExclPath=EXCL_ABSPATH;
+ break;
+ }
+ break;
+ default:
+ if (Switch[1]=='+')
+ {
+ InclFileAttr|=GetExclAttr(Switch+2);
+ InclAttrSet=true;
+ }
+ else
+ ExclFileAttr|=GetExclAttr(Switch+1);
+ break;
+ }
+ break;
+ case 'F':
+ if (Switch[1]==0)
+ FreshFiles=true;
+ else
+ BadSwitch(Switch);
+ break;
+ case 'H':
+ switch (toupperw(Switch[1]))
+ {
+ case 'P':
+ EncryptHeaders=true;
+ if (Switch[2]!=0)
+ {
+ Password.Set(Switch+2);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
+ }
+ else
+ if (!Password.IsSet())
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ eprintf(L"\n");
+ }
+ break;
+ default :
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'I':
+ if (wcsnicomp(Switch+1,L"LOG",3)==0)
+ {
+ wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
+ break;
+ }
+ if (wcsicomp(Switch+1,L"SND")==0)
+ {
+ Sound=true;
+ break;
+ }
+ if (wcsicomp(Switch+1,L"ERR")==0)
+ {
+ MsgStream=MSG_STDERR;
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"EML",3)==0)
+ {
+ wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
+ break;
+ }
+ if (wcsicomp(Switch+1,L"NUL")==0)
+ {
+ MsgStream=MSG_NULL;
+ break;
+ }
+ if (toupperw(Switch[1])=='D')
+ {
+ for (uint I=2;Switch[I]!=0;I++)
+ switch(toupperw(Switch[I]))
+ {
+ case 'Q':
+ MsgStream=MSG_ERRONLY;
+ break;
+ case 'C':
+ DisableCopyright=true;
+ break;
+ case 'D':
+ DisableDone=true;
+ break;
+ case 'P':
+ DisablePercentage=true;
+ break;
+ }
+ break;
+ }
+ if (wcsicomp(Switch+1,L"OFF")==0)
+ {
+ Shutdown=true;
+ break;
+ }
+ break;
+ case 'K':
+ switch(toupperw(Switch[1]))
+ {
+ case 'B':
+ KeepBroken=true;
+ break;
+ case 0:
+ Lock=true;
+ break;
+ }
+ break;
+ case 'M':
+ switch(toupperw(Switch[1]))
+ {
+ case 'C':
+ {
+ const wchar *Str=Switch+2;
+ if (*Str=='-')
+ for (uint I=0;IMaxPoolThreads || Threads<1)
+ BadSwitch(Switch);
+ else
+ {
+ }
+ break;
+#endif
+ default:
+ Method=Switch[1]-'0';
+ if (Method>5 || Method<0)
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'N':
+ case 'X':
+ if (Switch[1]!=0)
+ {
+ StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs;
+ if (Switch[1]=='@' && !IsWildcard(Switch))
+ {
+ RAR_CHARSET Charset=FilelistCharset;
+
+#if defined(_WIN_ALL) && !defined(GUI)
+ // for compatibility reasons we use OEM encoding
+ // in Win32 console version by default
+
+// if (Charset==RCH_DEFAULT)
+// Charset=RCH_OEM;
+#endif
+
+ ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true);
+ }
+ else
+ Args->AddString(Switch+1);
+ }
+ break;
+ case 'O':
+ switch(toupperw(Switch[1]))
+ {
+ case '+':
+ Overwrite=OVERWRITE_ALL;
+ break;
+ case '-':
+ Overwrite=OVERWRITE_NONE;
+ break;
+ case 0:
+ Overwrite=OVERWRITE_FORCE_ASK;
+ break;
+#ifdef _WIN_ALL
+ case 'C':
+ SetCompressedAttr=true;
+ break;
+#endif
+ case 'H':
+ SaveHardLinks=true;
+ break;
+
+
+#ifdef SAVE_LINKS
+ case 'L':
+ SaveSymLinks=true;
+ if (toupperw(Switch[2])=='A')
+ AbsoluteLinks=true;
+ break;
+#endif
+ case 'R':
+ Overwrite=OVERWRITE_AUTORENAME;
+ break;
+#ifdef _WIN_ALL
+ case 'S':
+ SaveStreams=true;
+ break;
+#endif
+ case 'W':
+ ProcessOwners=true;
+ break;
+ default :
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'P':
+ if (Switch[1]==0)
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ eprintf(L"\n");
+ }
+ else
+ {
+ Password.Set(Switch+1);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
+ }
+ break;
+#ifndef SFX_MODULE
+ case 'Q':
+ if (toupperw(Switch[1])=='O')
+ switch(toupperw(Switch[2]))
+ {
+ case 0:
+ QOpenMode=QOPEN_AUTO;
+ break;
+ case '-':
+ QOpenMode=QOPEN_NONE;
+ break;
+ case '+':
+ QOpenMode=QOPEN_ALWAYS;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ else
+ BadSwitch(Switch);
+ break;
+#endif
+ case 'R':
+ switch(toupperw(Switch[1]))
+ {
+ case 0:
+ Recurse=RECURSE_ALWAYS;
+ break;
+ case '-':
+ Recurse=RECURSE_DISABLE;
+ break;
+ case '0':
+ Recurse=RECURSE_WILDCARDS;
+ break;
+ case 'I':
+ {
+ Priority=atoiw(Switch+2);
+ if (Priority<0 || Priority>15)
+ BadSwitch(Switch);
+ const wchar *ChPtr=wcschr(Switch+2,':');
+ if (ChPtr!=NULL)
+ {
+ SleepTime=atoiw(ChPtr+1);
+ if (SleepTime>1000)
+ BadSwitch(Switch);
+ InitSystemOptions(SleepTime);
+ }
+ SetPriority(Priority);
+ }
+ break;
+ }
+ break;
+ case 'S':
+ if (IsDigit(Switch[1]))
+ {
+ Solid|=SOLID_COUNT;
+ SolidCount=atoiw(&Switch[1]);
+ }
+ else
+ switch(toupperw(Switch[1]))
+ {
+ case 0:
+ Solid|=SOLID_NORMAL;
+ break;
+ case '-':
+ Solid=SOLID_NONE;
+ break;
+ case 'E':
+ Solid|=SOLID_FILEEXT;
+ break;
+ case 'V':
+ Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT;
+ break;
+ case 'D':
+ Solid|=SOLID_VOLUME_DEPENDENT;
+ break;
+ case 'L':
+ if (IsDigit(Switch[2]))
+ FileSizeLess=atoilw(Switch+2);
+ break;
+ case 'M':
+ if (IsDigit(Switch[2]))
+ FileSizeMore=atoilw(Switch+2);
+ break;
+ case 'C':
+ {
+ bool AlreadyBad=false; // Avoid reporting "bad switch" several times.
+
+ RAR_CHARSET rch=RCH_DEFAULT;
+ switch(toupperw(Switch[2]))
+ {
+ case 'A':
+ rch=RCH_ANSI;
+ break;
+ case 'O':
+ rch=RCH_OEM;
+ break;
+ case 'U':
+ rch=RCH_UNICODE;
+ break;
+ default :
+ BadSwitch(Switch);
+ AlreadyBad=true;
+ break;
+ };
+ if (!AlreadyBad)
+ if (Switch[3]==0)
+ CommentCharset=FilelistCharset=ErrlogCharset=rch;
+ else
+ for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++)
+ switch(toupperw(Switch[I]))
+ {
+ case 'C':
+ CommentCharset=rch;
+ break;
+ case 'L':
+ FilelistCharset=rch;
+ break;
+ case 'R':
+ RedirectCharset=rch;
+ break;
+ default:
+ BadSwitch(Switch);
+ AlreadyBad=true;
+ break;
+ }
+ }
+ break;
+
+ }
+ break;
+ case 'T':
+ switch(toupperw(Switch[1]))
+ {
+ case 'K':
+ ArcTime=ARCTIME_KEEP;
+ break;
+ case 'L':
+ ArcTime=ARCTIME_LATEST;
+ break;
+ case 'O':
+ FileTimeBefore.SetAgeText(Switch+2);
+ break;
+ case 'N':
+ FileTimeAfter.SetAgeText(Switch+2);
+ break;
+ case 'B':
+ FileTimeBefore.SetIsoText(Switch+2);
+ break;
+ case 'A':
+ FileTimeAfter.SetIsoText(Switch+2);
+ break;
+ case 'S':
+ {
+ EXTTIME_MODE Mode=EXTTIME_HIGH3;
+ bool CommonMode=Switch[2]>='0' && Switch[2]<='4';
+ if (CommonMode)
+ Mode=(EXTTIME_MODE)(Switch[2]-'0');
+ if (Switch[2]=='-')
+ Mode=EXTTIME_NONE;
+ if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0)
+ xmtime=xctime=xatime=Mode;
+ else
+ {
+ if (Switch[3]>='0' && Switch[3]<='4')
+ Mode=(EXTTIME_MODE)(Switch[3]-'0');
+ if (Switch[3]=='-')
+ Mode=EXTTIME_NONE;
+ switch(toupperw(Switch[2]))
+ {
+ case 'M':
+ xmtime=Mode;
+ break;
+ case 'C':
+ xctime=Mode;
+ break;
+ case 'A':
+ xatime=Mode;
+ break;
+ }
+ }
+ }
+ break;
+ case '-':
+ Test=false;
+ break;
+ case 0:
+ Test=true;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'U':
+ if (Switch[1]==0)
+ UpdateFiles=true;
+ else
+ BadSwitch(Switch);
+ break;
+ case 'V':
+ switch(toupperw(Switch[1]))
+ {
+ case 'P':
+ VolumePause=true;
+ break;
+ case 'E':
+ if (toupperw(Switch[2])=='R')
+ VersionControl=atoiw(Switch+3)+1;
+ break;
+ case '-':
+ VolSize=0;
+ break;
+ default:
+ VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command.
+ break;
+ }
+ break;
+ case 'W':
+ wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
+ AddEndSlash(TempPath,ASIZE(TempPath));
+ break;
+ case 'Y':
+ AllYes=true;
+ break;
+ case 'Z':
+ if (Switch[1]==0)
+ {
+#ifndef GUI // stdin is not supported by WinRAR.
+ // If comment file is not specified, we read data from stdin.
+ wcscpy(CommentFile,L"stdin");
+#endif
+ }
+ else
+ wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
+ break;
+#ifndef GUI
+ case '?' :
+ OutHelp(RARX_SUCCESS);
+ break;
+#endif
+ default :
+ BadSwitch(Switch);
+ break;
+ }
+}
+#endif
+
+
+#if !defined(SFX_MODULE) && !defined(_ANDROID)
+void CommandData::BadSwitch(const wchar *Switch)
+{
+ mprintf(St(MUnknownOption),Switch);
+ ErrHandler.Exit(RARX_USERERROR);
+}
+#endif
+
+
+#ifndef GUI
+void CommandData::OutTitle()
+{
+ if (BareOutput || DisableCopyright)
+ return;
+#if defined(__GNUC__) && defined(SFX_MODULE)
+ mprintf(St(MCopyrightS));
+#else
+#ifndef SILENT
+ static bool TitleShown=false;
+ if (TitleShown)
+ return;
+ TitleShown=true;
+ wchar Version[50];
+ int Beta=RARVER_BETA;
+ if (Beta!=0)
+ swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
+ else
+ swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
+#ifdef UNRAR
+ mprintf(St(MUCopyright),Version,RARVER_YEAR);
+#else
+#endif
+#endif
+#endif
+}
+#endif
+
+
+inline bool CmpMSGID(MSGID i1,MSGID i2)
+{
+#ifdef MSGID_INT
+ return i1==i2;
+#else
+ // If MSGID is const char*, we cannot compare pointers only.
+ // Pointers to different instances of same string can differ,
+ // so we need to compare complete strings.
+ return wcscmp(i1,i2)==0;
+#endif
+}
+
+void CommandData::OutHelp(RAR_EXIT ExitCode)
+{
+#if !defined(GUI) && !defined(SILENT)
+ OutTitle();
+ static MSGID Help[]={
+#ifdef SFX_MODULE
+ // Console SFX switches definition.
+ MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
+#elif defined(UNRAR)
+ // UnRAR switches definition.
+ MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
+ MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
+ MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
+ MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
+ MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
+ MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
+ MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
+ MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
+ MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
+ MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,
+ MCHelpSwY
+#else
+#endif
+ };
+
+ for (uint I=0;IRewind();
+ while (Args->GetString(CurMask,ASIZE(CurMask)-1))
+ {
+ wchar *LastMaskChar=PointToLastChar(CurMask);
+ bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
+
+ if (Dir)
+ {
+ // CheckName is a directory.
+ if (DirMask)
+ {
+ // We process the directory and have the directory exclusion mask.
+ // So let's convert "mask\" to "mask" and process it normally.
+
+ *LastMaskChar=0;
+ }
+ else
+ {
+ // If mask has wildcards in name part and does not have the trailing
+ // '\' character, we cannot use it for directories.
+
+ if (IsWildcard(PointToName(CurMask)))
+ continue;
+ }
+ }
+ else
+ {
+ // If we process a file inside of directory excluded by "dirmask\".
+ // we want to exclude such file too. So we convert "dirmask\" to
+ // "dirmask\*". It is important for operations other than archiving
+ // with -x. When archiving with -x, directory matched by "dirmask\"
+ // is excluded from further scanning.
+
+ if (DirMask)
+ wcscat(CurMask,L"*");
+ }
+
+#ifndef SFX_MODULE
+ if (CheckFullPath && IsFullPath(CurMask))
+ {
+ // We do not need to do the special "*\" processing here, because
+ // unlike the "else" part of this "if", now we convert names to full
+ // format, so they all include the path, which is matched by "*\"
+ // correctly. Moreover, removing "*\" from mask would break
+ // the comparison, because now all names have the path.
+
+ if (*FullName==0)
+ ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
+ if (CmpName(CurMask,FullName,MatchMode))
+ return true;
+ }
+ else
+#endif
+ {
+ wchar NewName[NM+2],*CurName=Name;
+
+ // Important to convert before "*\" check below, so masks like
+ // d:*\something are processed properly.
+ wchar *CmpMask=ConvertPath(CurMask,NULL);
+
+ if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
+ {
+ // We want "*\name" to match 'name' not only in subdirectories,
+ // but also in the current directory. We convert the name
+ // from 'name' to '.\name' to be matched by "*\" part even if it is
+ // in current directory.
+ NewName[0]='.';
+ NewName[1]=CPATHDIVIDER;
+ wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
+ CurName=NewName;
+ }
+
+ if (CmpName(CmpMask,CurName,MatchMode))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+#ifndef SFX_MODULE
+// Now this function performs only one task and only in Windows version:
+// it skips symlinks to directories if -e1024 switch is specified.
+// Symlinks are skipped in ScanTree class, so their entire contents
+// is skipped too. Without this function we would check the attribute
+// only directly before archiving, so we would skip the symlink record,
+// but not the contents of symlinked directory.
+bool CommandData::ExclDirByAttr(uint FileAttr)
+{
+#ifdef _WIN_ALL
+ if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
+ (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
+ return true;
+#endif
+ return false;
+}
+#endif
+
+
+
+
+#ifndef SFX_MODULE
+// Return 'true' if we need to exclude the file from processing.
+bool CommandData::TimeCheck(RarTime &ft)
+{
+ if (FileTimeBefore.IsSet() && ft>=FileTimeBefore)
+ return true;
+ if (FileTimeAfter.IsSet() && ft<=FileTimeAfter)
+ return true;
+ return false;
+}
+#endif
+
+
+#ifndef SFX_MODULE
+// Return 'true' if we need to exclude the file from processing.
+bool CommandData::SizeCheck(int64 Size)
+{
+ if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
+ return(true);
+ if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
+ return(true);
+ return(false);
+}
+#endif
+
+
+
+
+int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
+ wchar *MatchedArg,uint MatchedArgSize)
+{
+ if (MatchedArg!=NULL && MatchedArgSize>0)
+ *MatchedArg=0;
+ if (wcslen(FileHead.FileName)>=NM)
+ return 0;
+ bool Dir=FileHead.Dir;
+ if (ExclCheck(FileHead.FileName,Dir,false,true))
+ return 0;
+#ifndef SFX_MODULE
+ if (TimeCheck(FileHead.mtime))
+ return 0;
+ if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0)
+ return 0;
+ if (!Dir && SizeCheck(FileHead.UnpSize))
+ return 0;
+#endif
+ wchar *ArgName;
+ FileArgs.Rewind();
+ for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
+ if (CmpName(ArgName,FileHead.FileName,MatchType))
+ {
+ if (ExactMatch!=NULL)
+ *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
+ if (MatchedArg!=NULL)
+ wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
+ return StringCount;
+ }
+ return 0;
+}
+
+
+#ifndef GUI
+void CommandData::ProcessCommand()
+{
+#ifndef SFX_MODULE
+
+ const wchar *SingleCharCommands=L"FUADPXETK";
+ if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
+ OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
+
+#ifdef _UNIX
+ if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
+#else
+ if (GetExt(ArcName)==NULL)
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
+#endif
+
+ if (wcschr(L"AFUMD",*Command)==NULL)
+ {
+ if (GenerateArcName)
+ GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false);
+
+ StringList ArcMasks;
+ ArcMasks.AddString(ArcName);
+ ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS);
+ FindData FindData;
+ while (Scan.GetNext(&FindData)==SCAN_SUCCESS)
+ AddArcName(FindData.Name);
+ }
+ else
+ AddArcName(ArcName);
+#endif
+
+ switch(Command[0])
+ {
+ case 'P':
+ case 'X':
+ case 'E':
+ case 'T':
+ case 'I':
+ {
+ CmdExtract Extract(this);
+ Extract.DoExtract();
+ }
+ break;
+#ifndef SILENT
+ case 'V':
+ case 'L':
+ ListArchive(this);
+ break;
+ default:
+ OutHelp(RARX_USERERROR);
+#endif
+ }
+ if (!BareOutput)
+ mprintf(L"\n");
+}
+#endif
+
+
+void CommandData::AddArcName(const wchar *Name)
+{
+ ArcNames.AddString(Name);
+}
+
+
+bool CommandData::GetArcName(wchar *Name,int MaxSize)
+{
+ return ArcNames.GetString(Name,MaxSize);
+}
+
+
+bool CommandData::IsSwitch(int Ch)
+{
+#if defined(_WIN_ALL) || defined(_EMX)
+ return(Ch=='-' || Ch=='/');
+#else
+ return(Ch=='-');
+#endif
+}
+
+
+#ifndef SFX_MODULE
+uint CommandData::GetExclAttr(const wchar *Str)
+{
+ if (IsDigit(*Str))
+ return(wcstol(Str,NULL,0));
+
+ uint Attr=0;
+ while (*Str!=0)
+ {
+ switch(toupperw(*Str))
+ {
+#ifdef _UNIX
+ case 'D':
+ Attr|=S_IFDIR;
+ break;
+ case 'V':
+ Attr|=S_IFCHR;
+ break;
+#elif defined(_WIN_ALL) || defined(_EMX)
+ case 'R':
+ Attr|=0x1;
+ break;
+ case 'H':
+ Attr|=0x2;
+ break;
+ case 'S':
+ Attr|=0x4;
+ break;
+ case 'D':
+ Attr|=0x10;
+ break;
+ case 'A':
+ Attr|=0x20;
+ break;
+#endif
+ }
+ Str++;
+ }
+ return Attr;
+}
+#endif
+
+
+
+
+#ifndef SFX_MODULE
+bool CommandData::CheckWinSize()
+{
+ // Define 0x100000000 as macro to avoid troubles with older compilers.
+ const uint64 MaxDictSize=INT32TO64(1,0);
+ // Limit the dictionary size to 4 GB.
+ for (uint64 I=0x10000;I<=MaxDictSize;I*=2)
+ if (WinSize==I)
+ return true;
+ WinSize=0x400000;
+ return false;
+}
+#endif
+
+
+#ifndef SFX_MODULE
+void CommandData::ReportWrongSwitches(RARFORMAT Format)
+{
+ if (Format==RARFMT15)
+ {
+ if (HashType!=HASH_CRC32)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4);
+#ifdef _WIN_ALL
+ if (SaveSymLinks)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4);
+#endif
+ if (SaveHardLinks)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4);
+
+#ifdef _WIN_ALL
+ // Do not report a wrong dictionary size here, because we are not sure
+ // yet about archive format. We can switch to RAR5 mode later
+ // if we update RAR5 archive.
+
+
+#endif
+ if (QOpenMode!=QOPEN_AUTO)
+ uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4);
+ }
+ if (Format==RARFMT50)
+ {
+ }
+}
+#endif
diff --git a/cmddata.hpp b/cmddata.hpp
new file mode 100644
index 0000000..8b54ce3
--- /dev/null
+++ b/cmddata.hpp
@@ -0,0 +1,64 @@
+#ifndef _RAR_CMDDATA_
+#define _RAR_CMDDATA_
+
+
+#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lzh;mp3;rar;taz;tgz;xz;z;zip"
+
+enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
+
+class CommandData:public RAROptions
+{
+ private:
+ void ProcessSwitchesString(const wchar *Str);
+ void ProcessSwitch(const wchar *Switch);
+ void BadSwitch(const wchar *Switch);
+ uint GetExclAttr(const wchar *Str);
+
+ bool FileLists;
+ bool NoMoreSwitches;
+ RAR_CMD_LIST_MODE ListMode;
+ bool BareOutput;
+ public:
+ CommandData();
+ void Init();
+
+ void ParseCommandLine(bool Preprocess,int argc, char *argv[]);
+ void ParseArg(wchar *ArgW);
+ void ParseDone();
+ void ParseEnvVar();
+ void ReadConfig();
+ void PreprocessArg(const wchar *Arg);
+ void OutTitle();
+ void OutHelp(RAR_EXIT ExitCode);
+ bool IsSwitch(int Ch);
+ bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
+ static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode);
+ bool ExclDirByAttr(uint FileAttr);
+ bool TimeCheck(RarTime &ft);
+ bool SizeCheck(int64 Size);
+ bool AnyFiltersActive();
+ int IsProcessFile(FileHeader &FileHead,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH,
+ wchar *MatchedArg=NULL,uint MatchedArgSize=0);
+ void ProcessCommand();
+ void AddArcName(const wchar *Name);
+ bool GetArcName(wchar *Name,int MaxSize);
+ bool CheckWinSize();
+
+ int GetRecoverySize(const wchar *Str,int DefSize);
+
+#ifndef SFX_MODULE
+ void ReportWrongSwitches(RARFORMAT Format);
+#endif
+
+ wchar Command[NM+16];
+
+ wchar ArcName[NM];
+
+ StringList FileArgs;
+ StringList ExclArgs;
+ StringList InclArgs;
+ StringList ArcNames;
+ StringList StoreArgs;
+};
+
+#endif
diff --git a/coder.cpp b/coder.cpp
new file mode 100644
index 0000000..9d971a8
--- /dev/null
+++ b/coder.cpp
@@ -0,0 +1,48 @@
+
+
+inline unsigned int RangeCoder::GetChar()
+{
+ return(UnpackRead->GetChar());
+}
+
+
+void RangeCoder::InitDecoder(Unpack *UnpackRead)
+{
+ RangeCoder::UnpackRead=UnpackRead;
+
+ low=code=0;
+ range=uint(-1);
+ for (int i=0;i < 4;i++)
+ code=(code << 8) | GetChar();
+}
+
+
+// (int) cast before "low" added only to suppress compiler warnings.
+#define ARI_DEC_NORMALIZE(code,low,range,read) \
+{ \
+ while ((low^(low+range))GetChar(); \
+ range <<= 8; \
+ low <<= 8; \
+ } \
+}
+
+
+inline int RangeCoder::GetCurrentCount()
+{
+ return (code-low)/(range /= SubRange.scale);
+}
+
+
+inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT)
+{
+ return (code-low)/(range >>= SHIFT);
+}
+
+
+inline void RangeCoder::Decode()
+{
+ low += range*SubRange.LowCount;
+ range *= SubRange.HighCount-SubRange.LowCount;
+}
diff --git a/coder.hpp b/coder.hpp
new file mode 100644
index 0000000..7b36ff2
--- /dev/null
+++ b/coder.hpp
@@ -0,0 +1,23 @@
+/****************************************************************************
+ * Contents: 'Carryless rangecoder' by Dmitry Subbotin *
+ ****************************************************************************/
+
+
+class RangeCoder
+{
+ public:
+ void InitDecoder(Unpack *UnpackRead);
+ inline int GetCurrentCount();
+ inline uint GetCurrentShiftCount(uint SHIFT);
+ inline void Decode();
+ inline void PutChar(unsigned int c);
+ inline unsigned int GetChar();
+
+ uint low, code, range;
+ struct SUBRANGE
+ {
+ uint LowCount, HighCount, scale;
+ } SubRange;
+
+ Unpack *UnpackRead;
+};
diff --git a/compress.hpp b/compress.hpp
new file mode 100644
index 0000000..8e4f1ed
--- /dev/null
+++ b/compress.hpp
@@ -0,0 +1,50 @@
+#ifndef _RAR_COMPRESS_
+#define _RAR_COMPRESS_
+
+// Combine pack and unpack constants to class to avoid polluting global
+// namespace with numerous short names.
+class PackDef
+{
+ public:
+ static const uint MAX_LZ_MATCH = 0x1001;
+ static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3.
+ static const uint LOW_DIST_REP_COUNT = 16;
+
+ static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */
+ static const uint DC = 64;
+ static const uint LDC = 16;
+ static const uint RC = 44;
+ static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC;
+ static const uint BC = 20;
+
+ static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */
+ static const uint DC30 = 60;
+ static const uint LDC30 = 17;
+ static const uint RC30 = 28;
+ static const uint BC30 = 20;
+ static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30;
+
+ static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */
+ static const uint DC20 = 48;
+ static const uint RC20 = 28;
+ static const uint BC20 = 19;
+ static const uint MC20 = 257;
+
+ // Largest alphabet size among all values listed above.
+ static const uint LARGEST_TABLE_SIZE = 306;
+
+ enum {
+ CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE,
+ CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA
+ };
+};
+
+
+enum FilterType {
+ // These values must not be changed, because we use them directly
+ // in RAR5 compression and decompression code.
+ FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM,
+ FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE
+};
+
+#endif
diff --git a/consio.cpp b/consio.cpp
new file mode 100644
index 0000000..243ebbc
--- /dev/null
+++ b/consio.cpp
@@ -0,0 +1,340 @@
+#include "rar.hpp"
+#include "log.cpp"
+
+static MESSAGE_TYPE MsgStream=MSG_STDOUT;
+static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
+
+const int MaxMsgSize=2*NM+2048;
+
+#ifdef _WIN_ALL
+static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
+#endif
+
+
+#ifdef _WIN_ALL
+static bool IsRedirected(DWORD nStdHandle)
+{
+ HANDLE hStd=GetStdHandle(nStdHandle);
+ DWORD Mode;
+ return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
+}
+#endif
+
+
+void InitConsole()
+{
+#ifdef _WIN_ALL
+ // We want messages like file names or progress percent to be printed
+ // immediately. Use only in Windows, in Unix they can cause wprintf %ls
+ // to fail with non-English strings.
+ setbuf(stdout,NULL);
+ setbuf(stderr,NULL);
+
+ // Detect if output is redirected and set output mode properly.
+ // We do not want to send Unicode output to files and especially to pipes
+ // like '|more', which cannot handle them correctly in Windows.
+ // In Unix console output is UTF-8 and it is handled correctly
+ // when redirecting, so no need to perform any adjustments.
+ StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
+ StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
+ StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
+#ifdef _MSC_VER
+ if (!StdoutRedirected)
+ _setmode(_fileno(stdout), _O_U16TEXT);
+ if (!StderrRedirected)
+ _setmode(_fileno(stderr), _O_U16TEXT);
+#endif
+#endif
+}
+
+
+void InitConsoleOptions(MESSAGE_TYPE MsgStream,RAR_CHARSET RedirectCharset)
+{
+ ::MsgStream=MsgStream;
+ ::RedirectCharset=RedirectCharset;
+}
+
+
+#ifndef SILENT
+static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
+{
+ // This buffer is for format string only, not for entire output,
+ // so it can be short enough.
+ wchar fmtw[1024];
+ PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
+#ifdef _WIN_ALL
+ safebuf wchar Msg[MaxMsgSize];
+ if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
+ {
+ HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ DWORD Written;
+ if (RedirectCharset==RCH_UNICODE)
+ WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
+ else
+ {
+ // Avoid Unicode for redirect in Windows, it does not work with pipes.
+ safebuf char MsgA[MaxMsgSize];
+ WideToChar(Msg,MsgA,ASIZE(MsgA));
+ if (RedirectCharset!=RCH_ANSI)
+ CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding.
+
+ // We already converted \n to \r\n above, so we use WriteFile instead
+ // of C library to avoid unnecessary additional conversion.
+ WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
+ }
+ return;
+ }
+ // MSVC2008 vfwprintf writes every character to console separately
+ // and it is too slow. We use direct WriteConsole call instead.
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
+ DWORD Written;
+ WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
+#else
+ vfwprintf(dest,fmtw,arglist);
+ // We do not use setbuf(NULL) in Unix (see comments in InitConsole).
+ fflush(dest);
+#endif
+}
+
+
+void mprintf(const wchar *fmt,...)
+{
+ if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
+ return;
+
+ fflush(stderr); // Ensure proper message order.
+
+ va_list arglist;
+ va_start(arglist,fmt);
+ FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
+ cvt_wprintf(dest,fmt,arglist);
+ va_end(arglist);
+}
+#endif
+
+
+#ifndef SILENT
+void eprintf(const wchar *fmt,...)
+{
+ if (MsgStream==MSG_NULL)
+ return;
+
+ fflush(stdout); // Ensure proper message order.
+
+ va_list arglist;
+ va_start(arglist,fmt);
+ cvt_wprintf(stderr,fmt,arglist);
+ va_end(arglist);
+}
+#endif
+
+
+#ifndef SILENT
+static void GetPasswordText(wchar *Str,uint MaxLength)
+{
+ if (MaxLength==0)
+ return;
+#ifdef _WIN_ALL
+ HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD ConInMode,ConOutMode;
+ DWORD Read=0;
+ GetConsoleMode(hConIn,&ConInMode);
+ GetConsoleMode(hConOut,&ConOutMode);
+ SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
+ SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
+
+ ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
+ Str[Read]=0;
+ SetConsoleMode(hConIn,ConInMode);
+ SetConsoleMode(hConOut,ConOutMode);
+#else
+ char StrA[MAXPASSWORD];
+#if defined(_EMX) || defined (__VMS)
+ fgets(StrA,ASIZE(StrA)-1,stdin);
+#elif defined(__sun)
+ strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
+#else
+ strncpyz(StrA,getpass(""),ASIZE(StrA));
+#endif
+ CharToWide(StrA,Str,MaxLength);
+ cleandata(StrA,sizeof(StrA));
+#endif
+ Str[MaxLength-1]=0;
+ RemoveLF(Str);
+}
+#endif
+
+
+#ifndef SILENT
+bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
+{
+ uiAlarm(UIALARM_QUESTION);
+
+ while (true)
+ {
+ if (Type==UIPASSWORD_GLOBAL)
+ eprintf(L"\n%s: ",St(MAskPsw));
+ else
+ eprintf(St(MAskPswFor),FileName);
+
+ wchar PlainPsw[MAXPASSWORD];
+ GetPasswordText(PlainPsw,ASIZE(PlainPsw));
+ if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL)
+ return false;
+ if (Type==UIPASSWORD_GLOBAL)
+ {
+ eprintf(St(MReAskPsw));
+ wchar CmpStr[MAXPASSWORD];
+ GetPasswordText(CmpStr,ASIZE(CmpStr));
+ if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
+ {
+ eprintf(St(MNotMatchPsw));
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ cleandata(CmpStr,sizeof(CmpStr));
+ continue;
+ }
+ cleandata(CmpStr,sizeof(CmpStr));
+ }
+ Password->Set(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ break;
+ }
+ return true;
+}
+#endif
+
+
+#ifndef SILENT
+bool getwstr(wchar *str,size_t n)
+{
+ // Print buffered prompt title function before waiting for input.
+ fflush(stderr);
+
+ *str=0;
+#if defined(_WIN_ALL)
+ // fgetws does not work well with non-English text in Windows,
+ // so we do not use it.
+ if (StdinRedirected) // ReadConsole does not work if redirected.
+ {
+ // fgets does not work well with pipes in Windows in our test.
+ // Let's use files.
+ Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
+ File SrcFile;
+ SrcFile.SetHandleType(FILE_HANDLESTD);
+ int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
+ if (ReadSize<=0)
+ {
+ // Looks like stdin is a null device. We can enter to infinite loop
+ // calling Ask(), so let's better exit.
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+ StrA[ReadSize-1]=0;
+ CharToWide(&StrA[0],str,n);
+ }
+ else
+ {
+ DWORD ReadSize=0;
+ if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
+ return false;
+ str[ReadSize]=0;
+ }
+#else
+ if (fgetws(str,n,stdin)==NULL)
+ ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
+#endif
+ RemoveLF(str);
+ return true;
+}
+#endif
+
+
+#ifndef SILENT
+// We allow this function to return 0 in case of invalid input,
+// because it might be convenient to press Enter to some not dangerous
+// prompts like "insert disk with next volume". We should call this function
+// again in case of 0 in dangerous prompt such as overwriting file.
+int Ask(const wchar *AskStr)
+{
+ uiAlarm(UIALARM_QUESTION);
+
+ const int MaxItems=10;
+ wchar Item[MaxItems][40];
+ int ItemKeyPos[MaxItems],NumItems=0;
+
+ for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
+ {
+ wchar *CurItem=Item[NumItems];
+ wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
+ wchar *EndItem=wcschr(CurItem,'_');
+ if (EndItem!=NULL)
+ *EndItem=0;
+ int KeyPos=0,CurKey;
+ while ((CurKey=CurItem[KeyPos])!=0)
+ {
+ bool Found=false;
+ for (int I=0;I4 ? L"\n":L" "):L", ");
+ int KeyPos=ItemKeyPos[I];
+ for (int J=0;J[{key};"{string}"p used to redefine
+ // a keyboard key on some terminals.
+ if (Data[J]=='\"')
+ return true;
+ if (!IsDigit(Data[J]) && Data[J]!=';')
+ break;
+ }
+ return false;
+}
+
+
+void OutComment(const wchar *Comment,size_t Size)
+{
+ if (IsCommentUnsafe(Comment,Size))
+ return;
+ const size_t MaxOutSize=0x400;
+ for (size_t I=0;I>1)^0xEDB88320 : (C>>1);
+ CRCTab[I]=C;
+ }
+}
+
+
+static void InitTables()
+{
+ InitCRC32(crc_tables[0]);
+
+ for (uint I=0;I<256;I++) // Build additional lookup tables.
+ {
+ uint C=crc_tables[0][I];
+ for (uint J=1;J<8;J++)
+ {
+ C=crc_tables[0][(byte)C]^(C>>8);
+ crc_tables[J][I]=C;
+ }
+ }
+}
+
+
+struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
+
+uint CRC32(uint StartCRC,const void *Addr,size_t Size)
+{
+ byte *Data=(byte *)Addr;
+
+ // Align Data to 8 for better performance.
+ for (;Size>0 && ((size_t)Data & 7);Size--,Data++)
+ StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ for (;Size>=8;Size-=8,Data+=8)
+ {
+#ifdef BIG_ENDIAN
+ StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
+ uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
+#else
+ StartCRC ^= *(uint32 *) Data;
+ uint NextData = *(uint32 *) (Data +4);
+#endif
+ StartCRC = crc_tables[7][(byte) StartCRC ] ^
+ crc_tables[6][(byte)(StartCRC >> 8) ] ^
+ crc_tables[5][(byte)(StartCRC >> 16)] ^
+ crc_tables[4][(byte)(StartCRC >> 24)] ^
+ crc_tables[3][(byte) NextData ] ^
+ crc_tables[2][(byte)(NextData >>8 ) ] ^
+ crc_tables[1][(byte)(NextData >> 16)] ^
+ crc_tables[0][(byte)(NextData >> 24)];
+ }
+
+ for (;Size>0;Size--,Data++) // Process left data.
+ StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ return StartCRC;
+}
+
+
+#ifndef SFX_MODULE
+// For RAR 1.4 archives in case somebody still has them.
+ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
+{
+ byte *Data=(byte *)Addr;
+ for (size_t I=0;I>15))&0xffff;
+ }
+ return StartCRC;
+}
+#endif
+
+
diff --git a/crc.hpp b/crc.hpp
new file mode 100644
index 0000000..d8fea28
--- /dev/null
+++ b/crc.hpp
@@ -0,0 +1,15 @@
+#ifndef _RAR_CRC_
+#define _RAR_CRC_
+
+// This function is only to intialize external CRC tables. We do not need to
+// call it before calculating CRC32.
+void InitCRC32(uint *CRCTab);
+
+uint CRC32(uint StartCRC,const void *Addr,size_t Size);
+
+#ifndef SFX_MODULE
+ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
+#endif
+
+
+#endif
diff --git a/crypt.cpp b/crypt.cpp
new file mode 100644
index 0000000..1a97640
--- /dev/null
+++ b/crypt.cpp
@@ -0,0 +1,129 @@
+#include "rar.hpp"
+
+#ifndef SFX_MODULE
+#include "crypt1.cpp"
+#include "crypt2.cpp"
+#endif
+#include "crypt3.cpp"
+#include "crypt5.cpp"
+
+
+CryptData::CryptData()
+{
+ Method=CRYPT_NONE;
+ memset(KDF3Cache,0,sizeof(KDF3Cache));
+ memset(KDF5Cache,0,sizeof(KDF5Cache));
+ KDF3CachePos=0;
+ KDF5CachePos=0;
+ memset(CRCTab,0,sizeof(CRCTab));
+}
+
+
+CryptData::~CryptData()
+{
+ cleandata(KDF3Cache,sizeof(KDF3Cache));
+ cleandata(KDF5Cache,sizeof(KDF5Cache));
+}
+
+
+
+
+void CryptData::DecryptBlock(byte *Buf,size_t Size)
+{
+ switch(Method)
+ {
+#ifndef SFX_MODULE
+ case CRYPT_RAR13:
+ Decrypt13(Buf,Size);
+ break;
+ case CRYPT_RAR15:
+ Crypt15(Buf,Size);
+ break;
+ case CRYPT_RAR20:
+ for (size_t I=0;IIsSet() || Method==CRYPT_NONE)
+ return false;
+
+ CryptData::Method=Method;
+
+ wchar PwdW[MAXPASSWORD];
+ Password->Get(PwdW,ASIZE(PwdW));
+ char PwdA[MAXPASSWORD];
+ WideToChar(PwdW,PwdA,ASIZE(PwdA));
+
+ switch(Method)
+ {
+#ifndef SFX_MODULE
+ case CRYPT_RAR13:
+ SetKey13(PwdA);
+ break;
+ case CRYPT_RAR15:
+ SetKey15(PwdA);
+ break;
+ case CRYPT_RAR20:
+ SetKey20(PwdA);
+ break;
+#endif
+ case CRYPT_RAR30:
+ SetKey30(Encrypt,Password,PwdW,Salt);
+ break;
+ case CRYPT_RAR50:
+ SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
+ break;
+ }
+ cleandata(PwdA,sizeof(PwdA));
+ cleandata(PwdW,sizeof(PwdW));
+ return true;
+}
+
+
+
+
+// Fill buffer with random data.
+void GetRnd(byte *RndBuf,size_t BufSize)
+{
+ bool Success=false;
+#if defined(_WIN_ALL)
+ HCRYPTPROV hProvider = 0;
+ if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ {
+ Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
+ CryptReleaseContext(hProvider, 0);
+ }
+#elif defined(_UNIX)
+ FILE *rndf = fopen("/dev/urandom", "r");
+ if (rndf!=NULL)
+ {
+ Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
+ fclose(rndf);
+ }
+#endif
+ // We use this code only as the last resort if code above failed.
+ if (!Success)
+ {
+ static uint Count=0;
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
+ uint64 Random=CurTime.GetRaw()+clock();
+ for (size_t I=0;I> ( (I & 7) * 8 ));
+ RndBuf[I]=byte( (RndByte ^ I) + Count++);
+ }
+ }
+}
diff --git a/crypt.hpp b/crypt.hpp
new file mode 100644
index 0000000..f6382ef
--- /dev/null
+++ b/crypt.hpp
@@ -0,0 +1,101 @@
+#ifndef _RAR_CRYPT_
+#define _RAR_CRYPT_
+
+
+enum CRYPT_METHOD {
+ CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
+};
+
+#define SIZE_SALT50 16
+#define SIZE_SALT30 8
+#define SIZE_INITV 16
+#define SIZE_PSWCHECK 8
+#define SIZE_PSWCHECK_CSUM 4
+
+#define CRYPT_BLOCK_SIZE 16
+#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
+
+#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
+#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
+#define CRYPT_VERSION 0 // Supported encryption version.
+
+
+class CryptData
+{
+ struct KDF5CacheItem
+ {
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT50];
+ byte Key[32];
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+ byte PswCheckValue[SHA256_DIGEST_SIZE];
+ byte HashKeyValue[SHA256_DIGEST_SIZE];
+ };
+
+ struct KDF3CacheItem
+ {
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT30];
+ byte Key[16];
+ byte Init[16];
+ bool SaltPresent;
+ };
+
+
+ private:
+ void SetKey13(const char *Password);
+ void Decrypt13(byte *Data,size_t Count);
+
+ void SetKey15(const char *Password);
+ void Crypt15(byte *Data,size_t Count);
+
+ void SetKey20(const char *Password);
+ void Swap20(byte *Ch1,byte *Ch2);
+ void UpdKeys20(byte *Buf);
+ void EncryptBlock20(byte *Buf);
+ void DecryptBlock20(byte *Buf);
+
+ void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
+ void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
+
+ KDF3CacheItem KDF3Cache[4];
+ uint KDF3CachePos;
+
+ KDF5CacheItem KDF5Cache[4];
+ uint KDF5CachePos;
+
+ CRYPT_METHOD Method;
+
+ Rijndael rin;
+
+ uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
+
+ byte SubstTable20[256];
+ uint Key20[4];
+
+ byte Key13[3];
+ ushort Key15[4];
+ public:
+ CryptData();
+ ~CryptData();
+ bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,
+ byte *HashKey,byte *PswCheck);
+ void SetAV15Encryption();
+ void SetCmt13Encryption();
+ void EncryptBlock(byte *Buf,size_t Size);
+ void DecryptBlock(byte *Buf,size_t Size);
+ static void SetSalt(byte *Salt,size_t SaltSize);
+};
+
+void GetRnd(byte *RndBuf,size_t BufSize);
+
+void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest);
+void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
+ size_t salt_len,byte *key, byte *Value1, byte *Value2,
+ uint rounds);
+
+void ConvertHashToMAC(HashValue *Value,byte *Key);
+
+#endif
diff --git a/crypt1.cpp b/crypt1.cpp
new file mode 100644
index 0000000..f840f47
--- /dev/null
+++ b/crypt1.cpp
@@ -0,0 +1,82 @@
+extern uint CRCTab[256];
+
+#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
+#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
+
+void CryptData::SetKey13(const char *Password)
+{
+ Key13[0]=Key13[1]=Key13[2]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key13[0]+=P;
+ Key13[1]^=P;
+ Key13[2]+=P;
+ Key13[2]=(byte)rol(Key13[2],1,8);
+ }
+}
+
+
+void CryptData::SetKey15(const char *Password)
+{
+ InitCRC32(CRCTab);
+ uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
+ Key15[0]=PswCRC&0xffff;
+ Key15[1]=(PswCRC>>16)&0xffff;
+ Key15[2]=Key15[3]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key15[2]^=P^CRCTab[P];
+ Key15[3]+=P+(CRCTab[P]>>16);
+ }
+}
+
+
+void CryptData::SetAV15Encryption()
+{
+ InitCRC32(CRCTab);
+ Method=CRYPT_RAR15;
+ Key15[0]=0x4765;
+ Key15[1]=0x9021;
+ Key15[2]=0x7382;
+ Key15[3]=0x5215;
+}
+
+
+void CryptData::SetCmt13Encryption()
+{
+ Method=CRYPT_RAR13;
+ Key13[0]=0;
+ Key13[1]=7;
+ Key13[2]=77;
+}
+
+
+void CryptData::Decrypt13(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key13[1]+=Key13[2];
+ Key13[0]+=Key13[1];
+ *Data-=Key13[0];
+ Data++;
+ }
+}
+
+
+void CryptData::Crypt15(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key15[0]+=0x1234;
+ Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
+ Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16;
+ Key15[0]^=Key15[2];
+ Key15[3]=ror(Key15[3]&0xffff,1,16)^Key15[1];
+ Key15[3]=ror(Key15[3]&0xffff,1,16);
+ Key15[0]^=Key15[3];
+ *Data^=(byte)(Key15[0]>>8);
+ Data++;
+ }
+}
diff --git a/crypt2.cpp b/crypt2.cpp
new file mode 100644
index 0000000..01d72f8
--- /dev/null
+++ b/crypt2.cpp
@@ -0,0 +1,195 @@
+#define NROUNDS 32
+
+#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
+ ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
+ ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
+ ((uint)SubstTable20[(int)(t>>24)&255]<<24) )
+
+
+static byte InitSubstTable20[256]={
+ 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
+ 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
+ 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
+ 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
+ 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
+ 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
+ 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
+ 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
+ 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
+ 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
+ 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
+ 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
+ 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
+ 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
+ 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
+ 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
+};
+
+
+void CryptData::SetKey20(const char *Password)
+{
+ InitCRC32(CRCTab);
+
+ char Psw[MAXPASSWORD];
+ strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
+ size_t PswLength=strlen(Psw);
+
+ Key20[0]=0xD3A3B879L;
+ Key20[1]=0x3F6D12F7L;
+ Key20[2]=0x7515A235L;
+ Key20[3]=0xA4E7F123L;
+
+ memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
+ for (int J=0;J<256;J++)
+ for (size_t I=0;I>8);
+ Buf[2]=(byte)(C>>16);
+ Buf[3]=(byte)(C>>24);
+ D^=Key20[1];
+ Buf[4]=(byte)D;
+ Buf[5]=(byte)(D>>8);
+ Buf[6]=(byte)(D>>16);
+ Buf[7]=(byte)(D>>24);
+ A^=Key20[2];
+ Buf[8]=(byte)A;
+ Buf[9]=(byte)(A>>8);
+ Buf[10]=(byte)(A>>16);
+ Buf[11]=(byte)(A>>24);
+ B^=Key20[3];
+ Buf[12]=(byte)B;
+ Buf[13]=(byte)(B>>8);
+ Buf[14]=(byte)(B>>16);
+ Buf[15]=(byte)(B>>24);
+#else
+ BufPtr[0]=C^Key20[0];
+ BufPtr[1]=D^Key20[1];
+ BufPtr[2]=A^Key20[2];
+ BufPtr[3]=B^Key20[3];
+#endif
+ UpdKeys20(Buf);
+}
+
+
+void CryptData::DecryptBlock20(byte *Buf)
+{
+ byte InBuf[16];
+ uint A,B,C,D,T,TA,TB;
+#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_MISALIGNED)
+ A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key20[0];
+ B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key20[1];
+ C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key20[2];
+ D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key20[3];
+#else
+ uint32 *BufPtr=(uint32 *)Buf;
+ A=BufPtr[0]^Key20[0];
+ B=BufPtr[1]^Key20[1];
+ C=BufPtr[2]^Key20[2];
+ D=BufPtr[3]^Key20[3];
+#endif
+ memcpy(InBuf,Buf,sizeof(InBuf));
+ for(int I=NROUNDS-1;I>=0;I--)
+ {
+ T=((C+rol(D,11,32))^Key20[I&3]);
+ TA=A^substLong(T);
+ T=((D^rol(C,17,32))+Key20[I&3]);
+ TB=B^substLong(T);
+ A=C;
+ B=D;
+ C=TA;
+ D=TB;
+ }
+#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_MISALIGNED)
+ C^=Key20[0];
+ Buf[0]=(byte)C;
+ Buf[1]=(byte)(C>>8);
+ Buf[2]=(byte)(C>>16);
+ Buf[3]=(byte)(C>>24);
+ D^=Key20[1];
+ Buf[4]=(byte)D;
+ Buf[5]=(byte)(D>>8);
+ Buf[6]=(byte)(D>>16);
+ Buf[7]=(byte)(D>>24);
+ A^=Key20[2];
+ Buf[8]=(byte)A;
+ Buf[9]=(byte)(A>>8);
+ Buf[10]=(byte)(A>>16);
+ Buf[11]=(byte)(A>>24);
+ B^=Key20[3];
+ Buf[12]=(byte)B;
+ Buf[13]=(byte)(B>>8);
+ Buf[14]=(byte)(B>>16);
+ Buf[15]=(byte)(B>>24);
+#else
+ BufPtr[0]=C^Key20[0];
+ BufPtr[1]=D^Key20[1];
+ BufPtr[2]=A^Key20[2];
+ BufPtr[3]=B^Key20[3];
+#endif
+ UpdKeys20(InBuf);
+}
+
+
+void CryptData::UpdKeys20(byte *Buf)
+{
+ for (int I=0;I<16;I+=4)
+ {
+ Key20[0]^=CRCTab[Buf[I]];
+ Key20[1]^=CRCTab[Buf[I+1]];
+ Key20[2]^=CRCTab[Buf[I+2]];
+ Key20[3]^=CRCTab[Buf[I+3]];
+ }
+}
+
+
+void CryptData::Swap20(byte *Ch1,byte *Ch2)
+{
+ byte Ch=*Ch1;
+ *Ch1=*Ch2;
+ *Ch2=Ch;
+}
diff --git a/crypt3.cpp b/crypt3.cpp
new file mode 100644
index 0000000..bcc7cfa
--- /dev/null
+++ b/crypt3.cpp
@@ -0,0 +1,66 @@
+void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
+{
+ byte AESKey[16],AESInit[16];
+
+ bool Cached=false;
+ for (uint I=0;I>8);
+ PswNum[2]=(byte)(I>>16);
+ sha1_process( &c, PswNum, 3, false);
+ if (I%(HashRounds/16)==0)
+ {
+ sha1_context tempc=c;
+ uint32 digest[5];
+ sha1_done( &tempc, digest, false);
+ AESInit[I/(HashRounds/16)]=(byte)digest[4];
+ }
+ }
+ uint32 digest[5];
+ sha1_done( &c, digest, false);
+ for (int I=0;I<4;I++)
+ for (int J=0;J<4;J++)
+ AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
+
+ KDF3Cache[KDF3CachePos].Pwd=*Password;
+ if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
+ memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
+ memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
+ memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
+ KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
+
+ cleandata(RawPsw,sizeof(RawPsw));
+ }
+ rin.Init(Encrypt, AESKey, 128, AESInit);
+ cleandata(AESKey,sizeof(AESKey));
+ cleandata(AESInit,sizeof(AESInit));
+}
+
diff --git a/crypt5.cpp b/crypt5.cpp
new file mode 100644
index 0000000..abc5d34
--- /dev/null
+++ b/crypt5.cpp
@@ -0,0 +1,195 @@
+void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest)
+{
+ const size_t Sha256BlockSize=64; // As defined in RFC 4868.
+
+ byte KeyHash[SHA256_DIGEST_SIZE];
+ if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
+ {
+ sha256_context KCtx;
+ sha256_init(&KCtx);
+ sha256_process(&KCtx, Key, KeyLength);
+ sha256_done(&KCtx, KeyHash);
+
+ Key = KeyHash;
+ KeyLength = SHA256_DIGEST_SIZE;
+ }
+
+ byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
+ KeyBuf[I] = Key[I] ^ 0x36;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x36;
+
+ sha256_context ICtx;
+ sha256_init(&ICtx);
+ sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ sha256_process(&ICtx, Data, DataLength); // Hash data.
+
+ byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
+ sha256_done(&ICtx, IDig);
+
+ sha256_context RCtx;
+ sha256_init(&RCtx);
+
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
+ KeyBuf[I] = Key[I] ^ 0x5c;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x5c;
+
+ sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
+
+ sha256_done(&RCtx, ResDigest);
+}
+
+
+// PBKDF2 for 32 byte key length. We generate the key for specified number
+// of iteration count also as two supplementary values (key for checksums
+// and password verification) for iterations+16 and iterations+32.
+void pbkdf2(const byte *Pwd, size_t PwdLength,
+ const byte *Salt, size_t SaltLength,
+ byte *Key, byte *V1, byte *V2, uint Count)
+{
+ const size_t MaxSalt=64;
+ byte SaltData[MaxSalt+4];
+ memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
+
+ SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
+ SaltData[SaltLength + 1] = 0;
+ SaltData[SaltLength + 2] = 0;
+ SaltData[SaltLength + 3] = 1;
+
+ // First iteration: HMAC of password, salt and block index (1).
+ byte U1[SHA256_DIGEST_SIZE];
+ hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1);
+ byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
+ memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
+
+ uint CurCount[] = { Count-1, 16, 16 };
+ byte *CurValue[] = { Key , V1, V2 };
+
+ byte U2[SHA256_DIGEST_SIZE];
+ for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
+ {
+ for (uint J = 0; J < CurCount[I]; J++)
+ {
+ hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2); // U2 = PRF (P, U1).
+ memcpy(U1, U2, sizeof(U1));
+ for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
+ Fn[K] ^= U1[K];
+ }
+ memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
+ }
+
+ cleandata(SaltData, sizeof(SaltData));
+ cleandata(Fn, sizeof(Fn));
+ cleandata(U1, sizeof(U1));
+ cleandata(U2, sizeof(U2));
+}
+
+
+void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
+ byte *PswCheck)
+{
+ if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
+ return;
+
+ byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
+ bool Found=false;
+ for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password &&
+ memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
+ {
+ SecHideData(Item->Key,sizeof(Item->Key),false,false);
+ memcpy(Key,Item->Key,sizeof(Key));
+ SecHideData(Item->Key,sizeof(Item->Key),true,false);
+
+ memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
+ memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
+ Found=true;
+ break;
+ }
+ }
+
+ if (!Found)
+ {
+ char PwdUtf[MAXPASSWORD*4];
+ WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
+
+ pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<Lg2Count=Lg2Cnt;
+ Item->Pwd=*Password;
+ memcpy(Item->Salt,Salt,SIZE_SALT50);
+ memcpy(Item->Key,Key,sizeof(Key));
+ memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
+ memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
+ SecHideData(Item->Key,sizeof(Key),true,false);
+ }
+ if (HashKey!=NULL)
+ memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
+ if (PswCheck!=NULL)
+ {
+ memset(PswCheck,0,SIZE_PSWCHECK);
+ for (uint I=0;IType==HASH_CRC32)
+ {
+ byte RawCRC[4];
+ RawPut4(Value->CRC32,RawCRC);
+ byte Digest[SHA256_DIGEST_SIZE];
+ hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest);
+ Value->CRC32=0;
+ for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8);
+ }
+ if (Value->Type==HASH_BLAKE2)
+ {
+ byte Digest[BLAKE2_DIGEST_SIZE];
+ hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest);
+ memcpy(Value->Digest,Digest,sizeof(Value->Digest));
+ }
+}
+
+
+#if 0
+static void TestPBKDF2();
+struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
+
+void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
+{
+ byte Key[32],V1[32],V2[32];
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
+ byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
+ mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
+ byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
+ mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
+ byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
+ mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
+}
+#endif
diff --git a/dll.cpp b/dll.cpp
new file mode 100644
index 0000000..a9aa112
--- /dev/null
+++ b/dll.cpp
@@ -0,0 +1,463 @@
+#include "rar.hpp"
+
+static int RarErrorToDll(RAR_EXIT ErrCode);
+
+struct DataSet
+{
+ CommandData Cmd;
+ Archive Arc;
+ CmdExtract Extract;
+ int OpenMode;
+ int HeaderSize;
+
+ DataSet():Arc(&Cmd),Extract(&Cmd) {};
+};
+
+
+HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
+{
+ RAROpenArchiveDataEx rx;
+ memset(&rx,0,sizeof(rx));
+ rx.ArcName=r->ArcName;
+ rx.OpenMode=r->OpenMode;
+ rx.CmtBuf=r->CmtBuf;
+ rx.CmtBufSize=r->CmtBufSize;
+ HANDLE hArc=RAROpenArchiveEx(&rx);
+ r->OpenResult=rx.OpenResult;
+ r->CmtSize=rx.CmtSize;
+ r->CmtState=rx.CmtState;
+ return hArc;
+}
+
+
+HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
+{
+ DataSet *Data=NULL;
+ try
+ {
+ r->OpenResult=0;
+ Data=new DataSet;
+ Data->Cmd.DllError=0;
+ Data->OpenMode=r->OpenMode;
+ Data->Cmd.FileArgs.AddString(L"*");
+
+ char AnsiArcName[NM];
+ *AnsiArcName=0;
+ if (r->ArcName!=NULL)
+ {
+ strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
+#ifdef _WIN_ALL
+ if (!AreFileApisANSI())
+ {
+ OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
+ AnsiArcName[ASIZE(AnsiArcName)-1]=0;
+ }
+#endif
+ }
+
+ wchar ArcName[NM];
+ GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
+
+ Data->Cmd.AddArcName(ArcName);
+ Data->Cmd.Overwrite=OVERWRITE_ALL;
+ Data->Cmd.VersionControl=1;
+
+ Data->Cmd.Callback=r->Callback;
+ Data->Cmd.UserData=r->UserData;
+
+ // Open shared mode is added by request of dll users, who need to
+ // browse and unpack archives while downloading.
+ Data->Cmd.OpenShared = true;
+ if (!Data->Arc.Open(ArcName,FMF_OPENSHARED))
+ {
+ r->OpenResult=ERAR_EOPEN;
+ delete Data;
+ return NULL;
+ }
+ if (!Data->Arc.IsArchive(true))
+ {
+ if (Data->Cmd.DllError!=0)
+ r->OpenResult=Data->Cmd.DllError;
+ else
+ {
+ RAR_EXIT ErrCode=ErrHandler.GetErrorCode();
+ if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING)
+ r->OpenResult=RarErrorToDll(ErrCode);
+ else
+ r->OpenResult=ERAR_BAD_ARCHIVE;
+ }
+ delete Data;
+ return NULL;
+ }
+ r->Flags=0;
+
+ if (Data->Arc.Volume)
+ r->Flags|=0x01;
+ if (Data->Arc.Locked)
+ r->Flags|=0x04;
+ if (Data->Arc.Solid)
+ r->Flags|=0x08;
+ if (Data->Arc.NewNumbering)
+ r->Flags|=0x10;
+ if (Data->Arc.Signed)
+ r->Flags|=0x20;
+ if (Data->Arc.Protected)
+ r->Flags|=0x40;
+ if (Data->Arc.Encrypted)
+ r->Flags|=0x80;
+ if (Data->Arc.FirstVolume)
+ r->Flags|=0x100;
+
+ Array CmtDataW;
+ if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
+ {
+ Array CmtData(CmtDataW.Size()*4+1);
+ memset(&CmtData[0],0,CmtData.Size());
+ WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
+ size_t Size=strlen(&CmtData[0])+1;
+
+ r->Flags|=2;
+ r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
+ r->CmtSize=(uint)Min(Size,r->CmtBufSize);
+ memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
+ if (Size<=r->CmtBufSize)
+ r->CmtBuf[r->CmtSize-1]=0;
+ }
+ else
+ r->CmtState=r->CmtSize=0;
+ Data->Extract.ExtractArchiveInit(Data->Arc);
+ return (HANDLE)Data;
+ }
+ catch (RAR_EXIT ErrCode)
+ {
+ if (Data!=NULL && Data->Cmd.DllError!=0)
+ r->OpenResult=Data->Cmd.DllError;
+ else
+ r->OpenResult=RarErrorToDll(ErrCode);
+ if (Data != NULL)
+ delete Data;
+ return NULL;
+ }
+ catch (std::bad_alloc&) // Catch 'new' exception.
+ {
+ r->OpenResult=ERAR_NO_MEMORY;
+ if (Data != NULL)
+ delete Data;
+ }
+ return NULL; // To make compilers happy.
+}
+
+
+int PASCAL RARCloseArchive(HANDLE hArcData)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ bool Success=Data==NULL ? false:Data->Arc.Close();
+ delete Data;
+ return Success ? ERAR_SUCCESS : ERAR_ECLOSE;
+}
+
+
+int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D)
+{
+ struct RARHeaderDataEx X;
+ memset(&X,0,sizeof(X));
+
+ int Code=RARReadHeaderEx(hArcData,&X);
+
+ strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName));
+ strncpyz(D->FileName,X.FileName,ASIZE(D->FileName));
+ D->Flags=X.Flags;
+ D->PackSize=X.PackSize;
+ D->UnpSize=X.UnpSize;
+ D->HostOS=X.HostOS;
+ D->FileCRC=X.FileCRC;
+ D->FileTime=X.FileTime;
+ D->UnpVer=X.UnpVer;
+ D->Method=X.Method;
+ D->FileAttr=X.FileAttr;
+ D->CmtSize=0;
+ D->CmtState=0;
+
+ return Code;
+}
+
+
+int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ try
+ {
+ if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
+ {
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
+ Data->Arc.EndArcHead.NextVolume)
+ if (MergeArchive(Data->Arc,NULL,false,'L'))
+ {
+ Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
+ return RARReadHeaderEx(hArcData,D);
+ }
+ else
+ return ERAR_EOPEN;
+
+ if (Data->Arc.BrokenHeader)
+ return ERAR_BAD_DATA;
+
+ // Might be necessary if RARSetPassword is still called instead of
+ // open callback for RAR5 archives and if password is invalid.
+ if (Data->Arc.FailedHeaderDecryption)
+ return ERAR_BAD_PASSWORD;
+
+ return ERAR_END_ARCHIVE;
+ }
+ FileHeader *hd=&Data->Arc.FileHead;
+ if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore)
+ {
+ int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
+ if (Code==0)
+ return RARReadHeaderEx(hArcData,D);
+ else
+ return Code;
+ }
+ wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW));
+ WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName));
+
+ wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW));
+ WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName));
+#ifdef _WIN_ALL
+ CharToOemA(D->FileName,D->FileName);
+#endif
+
+ D->Flags=0;
+ if (hd->SplitBefore)
+ D->Flags|=RHDF_SPLITBEFORE;
+ if (hd->SplitAfter)
+ D->Flags|=RHDF_SPLITAFTER;
+ if (hd->Encrypted)
+ D->Flags|=RHDF_ENCRYPTED;
+ if (hd->Solid)
+ D->Flags|=RHDF_SOLID;
+ if (hd->Dir)
+ D->Flags|=RHDF_DIRECTORY;
+
+ D->PackSize=uint(hd->PackSize & 0xffffffff);
+ D->PackSizeHigh=uint(hd->PackSize>>32);
+ D->UnpSize=uint(hd->UnpSize & 0xffffffff);
+ D->UnpSizeHigh=uint(hd->UnpSize>>32);
+ D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
+ if (Data->Arc.Format==RARFMT50)
+ D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big.
+ else
+ D->UnpVer=Data->Arc.FileHead.UnpVer;
+ D->FileCRC=hd->FileHash.CRC32;
+ D->FileTime=hd->mtime.GetDos();
+ D->Method=hd->Method+0x30;
+ D->FileAttr=hd->FileAttr;
+ D->CmtSize=0;
+ D->CmtState=0;
+
+ D->DictSize=uint(hd->WinSize/1024);
+
+ switch (hd->FileHash.Type)
+ {
+ case HASH_RAR14:
+ case HASH_CRC32:
+ D->HashType=RAR_HASH_CRC32;
+ break;
+ case HASH_BLAKE2:
+ D->HashType=RAR_HASH_BLAKE2;
+ memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
+ break;
+ default:
+ D->HashType=RAR_HASH_NONE;
+ break;
+ }
+
+ D->RedirType=hd->RedirType;
+ // RedirNameSize sanity check is useful in case some developer
+ // did not initialize Reserved area with 0 as required in docs.
+ // We have taken 'Redir*' fields from Reserved area. We may remove
+ // this RedirNameSize check sometimes later.
+ if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL &&
+ D->RedirNameSize>0 && D->RedirNameSize<100000)
+ wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize);
+ D->DirTarget=hd->DirTarget;
+ }
+ catch (RAR_EXIT ErrCode)
+ {
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
+ }
+ return ERAR_SUCCESS;
+}
+
+
+int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ try
+ {
+ Data->Cmd.DllError=0;
+ if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT ||
+ Operation==RAR_SKIP && !Data->Arc.Solid)
+ {
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
+ Data->Arc.FileHead.SplitAfter)
+ if (MergeArchive(Data->Arc,NULL,false,'L'))
+ {
+ Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
+ return ERAR_SUCCESS;
+ }
+ else
+ return ERAR_EOPEN;
+ Data->Arc.SeekToNext();
+ }
+ else
+ {
+ Data->Cmd.DllOpMode=Operation;
+
+ *Data->Cmd.ExtrPath=0;
+ *Data->Cmd.DllDestName=0;
+
+ if (DestPath!=NULL)
+ {
+ char ExtrPathA[NM];
+ strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2);
+#ifdef _WIN_ALL
+ // We must not apply OemToCharBuffA directly to DestPath,
+ // because we do not know DestPath length and OemToCharBuffA
+ // does not stop at 0.
+ OemToCharA(ExtrPathA,ExtrPathA);
+#endif
+ CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
+ AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
+ }
+ if (DestName!=NULL)
+ {
+ char DestNameA[NM];
+ strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2);
+#ifdef _WIN_ALL
+ // We must not apply OemToCharBuffA directly to DestName,
+ // because we do not know DestName length and OemToCharBuffA
+ // does not stop at 0.
+ OemToCharA(DestNameA,DestNameA);
+#endif
+ CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
+ }
+
+ if (DestPathW!=NULL)
+ {
+ wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
+ AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
+ }
+
+ if (DestNameW!=NULL)
+ wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
+
+ wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T");
+ Data->Cmd.Test=Operation!=RAR_EXTRACT;
+ bool Repeat=false;
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
+
+ // Now we process extra file information if any.
+ //
+ // Archive can be closed if we process volumes, next volume is missing
+ // and current one is already removed or deleted. So we need to check
+ // if archive is still open to avoid calling file operations on
+ // the invalid file handle. Some of our file operations like Seek()
+ // process such invalid handle correctly, some not.
+ while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
+ Data->Arc.GetHeaderType()==HEAD_SERVICE)
+ {
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
+ Data->Arc.SeekToNext();
+ }
+ Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
+ }
+ }
+ catch (std::bad_alloc&)
+ {
+ return ERAR_NO_MEMORY;
+ }
+ catch (RAR_EXIT ErrCode)
+ {
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
+ }
+ return Data->Cmd.DllError;
+}
+
+
+int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName)
+{
+ return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL));
+}
+
+
+int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName)
+{
+ return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName));
+}
+
+
+void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ Data->Cmd.ChangeVolProc=ChangeVolProc;
+}
+
+
+void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ Data->Cmd.Callback=Callback;
+ Data->Cmd.UserData=UserData;
+}
+
+
+void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ Data->Cmd.ProcessDataProc=ProcessDataProc;
+}
+
+
+#ifndef RAR_NOCRYPT
+void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
+{
+ DataSet *Data=(DataSet *)hArcData;
+ wchar PasswordW[MAXPASSWORD];
+ GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
+ Data->Cmd.Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+}
+#endif
+
+
+int PASCAL RARGetDllVersion()
+{
+ return RAR_DLL_VERSION;
+}
+
+
+static int RarErrorToDll(RAR_EXIT ErrCode)
+{
+ switch(ErrCode)
+ {
+ case RARX_FATAL:
+ return ERAR_EREAD;
+ case RARX_CRC:
+ return ERAR_BAD_DATA;
+ case RARX_WRITE:
+ return ERAR_EWRITE;
+ case RARX_OPEN:
+ return ERAR_EOPEN;
+ case RARX_CREATE:
+ return ERAR_ECREATE;
+ case RARX_MEMORY:
+ return ERAR_NO_MEMORY;
+ case RARX_BADPWD:
+ return ERAR_BAD_PASSWORD;
+ case RARX_SUCCESS:
+ return ERAR_SUCCESS; // 0.
+ default:
+ return ERAR_UNKNOWN;
+ }
+}
diff --git a/dll.def b/dll.def
new file mode 100644
index 0000000..54420e2
--- /dev/null
+++ b/dll.def
@@ -0,0 +1,12 @@
+EXPORTS
+ RAROpenArchive
+ RAROpenArchiveEx
+ RARCloseArchive
+ RARReadHeader
+ RARReadHeaderEx
+ RARProcessFile
+ RARSetCallback
+ RARSetChangeVolProc
+ RARSetProcessDataProc
+ RARSetPassword
+ RARGetDllVersion
diff --git a/dll.hpp b/dll.hpp
new file mode 100644
index 0000000..8dc975e
--- /dev/null
+++ b/dll.hpp
@@ -0,0 +1,169 @@
+#ifndef _UNRAR_DLL_
+#define _UNRAR_DLL_
+
+#pragma pack(1)
+
+#define ERAR_SUCCESS 0
+#define ERAR_END_ARCHIVE 10
+#define ERAR_NO_MEMORY 11
+#define ERAR_BAD_DATA 12
+#define ERAR_BAD_ARCHIVE 13
+#define ERAR_UNKNOWN_FORMAT 14
+#define ERAR_EOPEN 15
+#define ERAR_ECREATE 16
+#define ERAR_ECLOSE 17
+#define ERAR_EREAD 18
+#define ERAR_EWRITE 19
+#define ERAR_SMALL_BUF 20
+#define ERAR_UNKNOWN 21
+#define ERAR_MISSING_PASSWORD 22
+#define ERAR_EREFERENCE 23
+#define ERAR_BAD_PASSWORD 24
+
+#define RAR_OM_LIST 0
+#define RAR_OM_EXTRACT 1
+#define RAR_OM_LIST_INCSPLIT 2
+
+#define RAR_SKIP 0
+#define RAR_TEST 1
+#define RAR_EXTRACT 2
+
+#define RAR_VOL_ASK 0
+#define RAR_VOL_NOTIFY 1
+
+#define RAR_DLL_VERSION 7
+
+#define RAR_HASH_NONE 0
+#define RAR_HASH_CRC32 1
+#define RAR_HASH_BLAKE2 2
+
+
+#ifdef _UNIX
+#define CALLBACK
+#define PASCAL
+#define LONG long
+#define HANDLE void *
+#define LPARAM long
+#define UINT unsigned int
+#endif
+
+#define RHDF_SPLITBEFORE 0x01
+#define RHDF_SPLITAFTER 0x02
+#define RHDF_ENCRYPTED 0x04
+#define RHDF_SOLID 0x10
+#define RHDF_DIRECTORY 0x20
+
+
+struct RARHeaderData
+{
+ char ArcName[260];
+ char FileName[260];
+ unsigned int Flags;
+ unsigned int PackSize;
+ unsigned int UnpSize;
+ unsigned int HostOS;
+ unsigned int FileCRC;
+ unsigned int FileTime;
+ unsigned int UnpVer;
+ unsigned int Method;
+ unsigned int FileAttr;
+ char *CmtBuf;
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+};
+
+
+struct RARHeaderDataEx
+{
+ char ArcName[1024];
+ wchar_t ArcNameW[1024];
+ char FileName[1024];
+ wchar_t FileNameW[1024];
+ unsigned int Flags;
+ unsigned int PackSize;
+ unsigned int PackSizeHigh;
+ unsigned int UnpSize;
+ unsigned int UnpSizeHigh;
+ unsigned int HostOS;
+ unsigned int FileCRC;
+ unsigned int FileTime;
+ unsigned int UnpVer;
+ unsigned int Method;
+ unsigned int FileAttr;
+ char *CmtBuf;
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+ unsigned int DictSize;
+ unsigned int HashType;
+ char Hash[32];
+ unsigned int RedirType;
+ wchar_t *RedirName;
+ unsigned int RedirNameSize;
+ unsigned int DirTarget;
+ unsigned int Reserved[994];
+};
+
+
+struct RAROpenArchiveData
+{
+ char *ArcName;
+ unsigned int OpenMode;
+ unsigned int OpenResult;
+ char *CmtBuf;
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+};
+
+typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
+
+struct RAROpenArchiveDataEx
+{
+ char *ArcName;
+ wchar_t *ArcNameW;
+ unsigned int OpenMode;
+ unsigned int OpenResult;
+ char *CmtBuf;
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+ unsigned int Flags;
+ UNRARCALLBACK Callback;
+ LPARAM UserData;
+ unsigned int Reserved[28];
+};
+
+enum UNRARCALLBACK_MESSAGES {
+ UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
+ UCM_NEEDPASSWORDW
+};
+
+typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
+typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData);
+HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData);
+int PASCAL RARCloseArchive(HANDLE hArcData);
+int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData);
+int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
+int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
+int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName);
+void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData);
+void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc);
+void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc);
+void PASCAL RARSetPassword(HANDLE hArcData,char *Password);
+int PASCAL RARGetDllVersion();
+
+#ifdef __cplusplus
+}
+#endif
+
+#pragma pack()
+
+#endif
diff --git a/dll.rc b/dll.rc
new file mode 100644
index 0000000..391864a
--- /dev/null
+++ b/dll.rc
@@ -0,0 +1,28 @@
+#include
+#include
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 5, 31, 100, 1864
+PRODUCTVERSION 5, 31, 100, 1864
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "Alexander Roshal\0"
+ VALUE "ProductName", "RAR decompression library\0"
+ VALUE "FileDescription", "RAR decompression library\0"
+ VALUE "FileVersion", "5.31.0\0"
+ VALUE "ProductVersion", "5.31.0\0"
+ VALUE "LegalCopyright", "Copyright � Alexander Roshal 1993-2016\0"
+ VALUE "OriginalFilename", "Unrar.dll\0"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04E4
+ }
+}
+
diff --git a/encname.cpp b/encname.cpp
new file mode 100644
index 0000000..4e42a75
--- /dev/null
+++ b/encname.cpp
@@ -0,0 +1,57 @@
+#include "rar.hpp"
+
+EncodeFileName::EncodeFileName()
+{
+ Flags=0;
+ FlagBits=0;
+ FlagsPos=0;
+ DestSize=0;
+}
+
+
+
+
+void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,
+ size_t MaxDecSize)
+{
+ size_t EncPos=0,DecPos=0;
+ byte HighByte=EncName[EncPos++];
+ while (EncPos>6)
+ {
+ case 0:
+ NameW[DecPos++]=EncName[EncPos++];
+ break;
+ case 1:
+ NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
+ break;
+ case 2:
+ NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
+ EncPos+=2;
+ break;
+ case 3:
+ {
+ int Length=EncName[EncPos++];
+ if (Length & 0x80)
+ {
+ byte Correction=EncName[EncPos++];
+ for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos1)
+ exit(RARX_USERBREAK);
+ // Otherwise return from signal handler and let Wait() function to close
+ // files and quit. We cannot use the same approach as in Windows,
+ // because Unix signal handler can block execution of our main code.
+#endif
+
+#if defined(_WIN_ALL) && !defined(_MSC_VER)
+ // never reached, just to avoid a compiler warning
+ return TRUE;
+#endif
+}
+#endif
+
+
+void ErrorHandler::SetSignalHandlers(bool Enable)
+{
+ EnableBreak=Enable;
+#ifndef GUI
+#ifdef _WIN_ALL
+ SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
+#else
+ signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
+ signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
+#endif
+#endif
+}
+
+
+void ErrorHandler::Throw(RAR_EXIT Code)
+{
+ if (Code==RARX_USERBREAK && !EnableBreak)
+ return;
+#if !defined(GUI) && !defined(SILENT)
+ // Do not write "aborted" when just displaying online help.
+ if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
+ mprintf(L"\n%s\n",St(MProgAborted));
+#endif
+ SetErrorCode(Code);
+ throw Code;
+}
+
+
+void ErrorHandler::SysErrMsg()
+{
+#if !defined(SFX_MODULE) && !defined(SILENT)
+#ifdef _WIN_ALL
+ wchar *lpMsgBuf=NULL;
+ int ErrType=GetLastError();
+ if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf,0,NULL))
+ {
+ wchar *CurMsg=lpMsgBuf;
+ while (CurMsg!=NULL)
+ {
+ while (*CurMsg=='\r' || *CurMsg=='\n')
+ CurMsg++;
+ if (*CurMsg==0)
+ break;
+ wchar *EndMsg=wcschr(CurMsg,'\r');
+ if (EndMsg==NULL)
+ EndMsg=wcschr(CurMsg,'\n');
+ if (EndMsg!=NULL)
+ {
+ *EndMsg=0;
+ EndMsg++;
+ }
+ uiMsg(UIERROR_SYSERRMSG,CurMsg);
+ CurMsg=EndMsg;
+ }
+ }
+ LocalFree( lpMsgBuf );
+#endif
+
+#if defined(_UNIX) || defined(_EMX)
+ if (errno!=0)
+ {
+#ifdef _ANDROID
+ // Android NDK sets errno to confusing "not a typewriter" ENOTTY code
+ // after write error reported by write().
+ if (errno == ENOTTY)
+ return;
+#endif
+ char *err=strerror(errno);
+ if (err!=NULL)
+ {
+ wchar Msg[1024];
+ CharToWide(err,Msg,ASIZE(Msg));
+ uiMsg(UIERROR_SYSERRMSG,Msg);
+ }
+ }
+#endif
+
+#endif
+}
+
+
+int ErrorHandler::GetSystemErrorCode()
+{
+#ifdef _WIN_ALL
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
+
+void ErrorHandler::SetSystemErrorCode(int Code)
+{
+#ifdef _WIN_ALL
+ SetLastError(Code);
+#else
+ errno=Code;
+#endif
+}
diff --git a/errhnd.hpp b/errhnd.hpp
new file mode 100644
index 0000000..26df96d
--- /dev/null
+++ b/errhnd.hpp
@@ -0,0 +1,70 @@
+#ifndef _RAR_ERRHANDLER_
+#define _RAR_ERRHANDLER_
+
+enum RAR_EXIT // RAR exit code.
+{
+ RARX_SUCCESS = 0,
+ RARX_WARNING = 1,
+ RARX_FATAL = 2,
+ RARX_CRC = 3,
+ RARX_LOCK = 4,
+ RARX_WRITE = 5,
+ RARX_OPEN = 6,
+ RARX_USERERROR = 7,
+ RARX_MEMORY = 8,
+ RARX_CREATE = 9,
+ RARX_NOFILES = 10,
+ RARX_BADPWD = 11,
+ RARX_USERBREAK = 255
+};
+
+class ErrorHandler
+{
+ private:
+ RAR_EXIT ExitCode;
+ uint ErrCount;
+ bool EnableBreak;
+ bool Silent;
+ bool DisableShutdown; // Shutdown is not suitable after last error.
+ public:
+ ErrorHandler();
+ void Clean();
+ void MemoryError();
+ void OpenError(const wchar *FileName);
+ void CloseError(const wchar *FileName);
+ void ReadError(const wchar *FileName);
+ bool AskRepeatRead(const wchar *FileName);
+ void WriteError(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorFAT(const wchar *FileName);
+ bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
+ void SeekError(const wchar *FileName);
+ void GeneralErrMsg(const wchar *fmt,...);
+ void MemoryErrorMsg();
+ void OpenErrorMsg(const wchar *FileName);
+ void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void CreateErrorMsg(const wchar *FileName);
+ void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ReadErrorMsg(const wchar *FileName);
+ void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ArcBrokenMsg(const wchar *ArcName);
+ void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
+ void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
+ void Exit(RAR_EXIT ExitCode);
+ void SetErrorCode(RAR_EXIT Code);
+ RAR_EXIT GetErrorCode() {return ExitCode;}
+ uint GetErrorCount() {return ErrCount;}
+ void SetSignalHandlers(bool Enable);
+ void Throw(RAR_EXIT Code);
+ void SetSilent(bool Mode) {Silent=Mode;};
+ void SysErrMsg();
+ int GetSystemErrorCode();
+ void SetSystemErrorCode(int Code);
+ bool IsShutdownEnabled() {return !DisableShutdown;}
+
+ bool UserBreak; // Ctrl+Break is pressed.
+ bool MainExit; // main() is completed.
+};
+
+
+#endif
diff --git a/extinfo.cpp b/extinfo.cpp
new file mode 100644
index 0000000..15130ad
--- /dev/null
+++ b/extinfo.cpp
@@ -0,0 +1,111 @@
+#include "rar.hpp"
+
+#include "hardlinks.cpp"
+#include "win32stm.cpp"
+
+#ifdef _WIN_ALL
+#include "win32acl.cpp"
+#include "win32lnk.cpp"
+#endif
+
+#ifdef _UNIX
+#include "uowners.cpp"
+#ifdef SAVE_LINKS
+#include "ulinks.cpp"
+#endif
+#endif
+
+
+
+#ifndef SFX_MODULE
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
+{
+ if (Cmd->Test)
+ return;
+ switch(Arc.SubBlockHead.SubType)
+ {
+#ifdef _UNIX
+ case UO_HEAD:
+ if (Cmd->ProcessOwners)
+ ExtractUnixOwner20(Arc,Name);
+ break;
+#endif
+#ifdef _WIN_ALL
+ case NTACL_HEAD:
+ if (Cmd->ProcessOwners)
+ ExtractACL20(Arc,Name);
+ break;
+ case STREAM_HEAD:
+ ExtractStreams20(Arc,Name);
+ break;
+#endif
+ }
+}
+#endif
+
+
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
+{
+#ifdef _UNIX
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
+ Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
+ ExtractUnixOwner30(Arc,Name);
+#endif
+#ifdef _WIN_ALL
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
+ ExtractACL(Arc,Name);
+ if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
+ ExtractStreams(Arc,Name,Cmd->Test);
+#endif
+}
+
+
+
+
+bool IsRelativeSymlinkSafe(const wchar *SrcName,const wchar *TargetName)
+{
+ if (IsFullRootPath(SrcName))
+ return false;
+ int AllowedDepth=0;
+ while (*SrcName!=0)
+ {
+ if (IsPathDiv(SrcName[0]) && SrcName[1]!=0 && !IsPathDiv(SrcName[1]))
+ {
+ bool Dot=SrcName[1]=='.' && (IsPathDiv(SrcName[2]) || SrcName[2]==0);
+ bool Dot2=SrcName[1]=='.' && SrcName[2]=='.' && (IsPathDiv(SrcName[3]) || SrcName[3]==0);
+ if (!Dot && !Dot2)
+ AllowedDepth++;
+ }
+ SrcName++;
+ }
+ if (IsFullRootPath(TargetName)) // Catch root dir based /path/file paths.
+ return false;
+ for (int Pos=0;*TargetName!=0;Pos++)
+ {
+ bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
+ (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
+ (Pos==0 || IsPathDiv(*(TargetName-1)));
+ if (Dot2)
+ AllowedDepth--;
+ TargetName++;
+ }
+ return AllowedDepth>=0;
+}
+
+
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+{
+#if defined(SAVE_LINKS) && defined(_UNIX)
+ // For RAR 3.x archives we process links even in test mode to skip link data.
+ if (Arc.Format==RARFMT15)
+ return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
+ if (Arc.Format==RARFMT50)
+ return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
+#elif defined _WIN_ALL
+ // RAR 5.0 archives store link information in file header, so there is
+ // no need to additionally test it if we do not create a file.
+ if (Arc.Format==RARFMT50)
+ return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
+#endif
+ return false;
+}
diff --git a/extinfo.hpp b/extinfo.hpp
new file mode 100644
index 0000000..b974bf4
--- /dev/null
+++ b/extinfo.hpp
@@ -0,0 +1,21 @@
+#ifndef _RAR_EXTINFO_
+#define _RAR_EXTINFO_
+
+bool IsRelativeSymlinkSafe(const wchar *SrcName,const wchar *TargetName);
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
+#ifdef _UNIX
+void SetUnixOwner(Archive &Arc,const wchar *FileName);
+#endif
+
+bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+
+void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
+
+#ifdef _WIN_ALL
+bool SetPrivilege(LPCTSTR PrivName);
+#endif
+
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
+
+#endif
diff --git a/extract.cpp b/extract.cpp
new file mode 100644
index 0000000..2f6964e
--- /dev/null
+++ b/extract.cpp
@@ -0,0 +1,1131 @@
+#include "rar.hpp"
+
+CmdExtract::CmdExtract(CommandData *Cmd)
+{
+ CmdExtract::Cmd=Cmd;
+
+ *ArcName=0;
+
+ *DestFileName=0;
+
+ TotalFileCount=0;
+ Unp=new Unpack(&DataIO);
+#ifdef RAR_SMP
+ Unp->SetThreads(Cmd->Threads);
+#endif
+}
+
+
+CmdExtract::~CmdExtract()
+{
+ delete Unp;
+}
+
+
+void CmdExtract::DoExtract()
+{
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ Fat32=NotFat32=false;
+#endif
+ PasswordCancelled=false;
+ DataIO.SetCurrentCommand(Cmd->Command[0]);
+
+ FindData FD;
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ if (FindFile::FastFind(ArcName,&FD))
+ DataIO.TotalArcSize+=FD.Size;
+
+ Cmd->ArcNames.Rewind();
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ {
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean(); // Clean user entered password before processing next archive.
+ while (true)
+ {
+ EXTRACT_ARC_CODE Code=ExtractArchive();
+ if (Code!=EXTRACT_ARC_REPEAT)
+ break;
+ }
+ if (FindFile::FastFind(ArcName,&FD))
+ DataIO.ProcessedArcSize+=FD.Size;
+ }
+
+ // Clean user entered password. Not really required, just for extra safety.
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean();
+
+ if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
+ ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
+ {
+ if (!PasswordCancelled)
+ uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName);
+ ErrHandler.SetErrorCode(RARX_NOFILES);
+ }
+#ifndef GUI
+ else
+ if (!Cmd->DisableDone)
+ if (Cmd->Command[0]=='I')
+ mprintf(St(MDone));
+ else
+ if (ErrHandler.GetErrorCount()==0)
+ mprintf(St(MExtrAllOk));
+ else
+ mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount());
+#endif
+}
+
+
+void CmdExtract::ExtractArchiveInit(Archive &Arc)
+{
+ DataIO.UnpArcSize=Arc.FileLength();
+
+ FileCount=0;
+ MatchedArgs=0;
+#ifndef SFX_MODULE
+ FirstFile=true;
+#endif
+
+ PasswordAll=(Cmd->Password.IsSet());
+
+ DataIO.UnpVolume=false;
+
+ PrevProcessed=false;
+ AllMatchesExact=true;
+ ReconstructDone=false;
+ AnySolidDataUnpackedWell=false;
+
+ StartTime.SetCurrentTime();
+}
+
+
+EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
+{
+ Archive Arc(Cmd);
+ if (!Arc.WOpen(ArcName))
+ return EXTRACT_ARC_NEXT;
+
+ if (!Arc.IsArchive(true))
+ {
+#ifndef GUI
+ mprintf(St(MNotRAR),ArcName);
+#endif
+ if (CmpExt(ArcName,L"rar"))
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return EXTRACT_ARC_NEXT;
+ }
+
+ if (Arc.FailedHeaderDecryption) // Bad archive password.
+ return EXTRACT_ARC_NEXT;
+
+#ifndef SFX_MODULE
+ if (Arc.Volume && !Arc.FirstVolume)
+ {
+ wchar FirstVolName[NM];
+ VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);
+
+ // If several volume names from same volume set are specified
+ // and current volume is not first in set and first volume is present
+ // and specified too, let's skip the current volume.
+ if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
+ Cmd->ArcNames.Search(FirstVolName,false))
+ return EXTRACT_ARC_NEXT;
+ }
+#endif
+
+ int64 VolumeSetSize=0; // Total size of volumes after the current volume.
+
+ if (Arc.Volume)
+ {
+ // Calculate the total size of all accessible volumes.
+ // This size is necessary to display the correct total progress indicator.
+
+ wchar NextName[NM];
+ wcscpy(NextName,Arc.FileName);
+
+ while (true)
+ {
+ // First volume is already added to DataIO.TotalArcSize
+ // in initial TotalArcSize calculation in DoExtract.
+ // So we skip it and start from second volume.
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ FindData FD;
+ if (FindFile::FastFind(NextName,&FD))
+ VolumeSetSize+=FD.Size;
+ else
+ break;
+ }
+ DataIO.TotalArcSize+=VolumeSetSize;
+ }
+
+ ExtractArchiveInit(Arc);
+
+ if (*Cmd->Command=='T' || *Cmd->Command=='I')
+ Cmd->Test=true;
+
+
+ if (*Cmd->Command=='I')
+ {
+#ifndef GUI
+ Cmd->DisablePercentage=true;
+#endif
+ }
+ else
+ uiStartArchiveExtract(!Cmd->Test,ArcName);
+
+ Arc.ViewComment();
+
+
+ while (1)
+ {
+ size_t Size=Arc.ReadHeader();
+
+
+ bool Repeat=false;
+ if (!ExtractCurrentFile(Arc,Size,Repeat))
+ if (Repeat)
+ {
+ // If we started extraction from not first volume and need to
+ // restart it from first, we must correct DataIO.TotalArcSize
+ // for correct total progress display. We subtract the size
+ // of current volume and all volumes after it and add the size
+ // of new (first) volume.
+ FindData OldArc,NewArc;
+ if (FindFile::FastFind(Arc.FileName,&OldArc) &&
+ FindFile::FastFind(ArcName,&NewArc))
+ DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size;
+ return EXTRACT_ARC_REPEAT;
+ }
+ else
+ break;
+ }
+
+
+ return EXTRACT_ARC_NEXT;
+}
+
+
+bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
+{
+ // We can get negative sizes in corrupt archive and it is unacceptable
+ // for size comparisons in CmdExtract::UnstoreFile and ComprDataIO::UnpRead,
+ // where we cast sizes to size_t and can exceed another read or available
+ // size. We could fix it when reading an archive. But we prefer to do it
+ // here, because this function is called directly in unrar.dll, so we fix
+ // bad parameters passed to dll. Also we want to see real negative sizes
+ // in the listing of corrupt archive.
+ if (Arc.FileHead.PackSize<0)
+ Arc.FileHead.PackSize=0;
+ if (Arc.FileHead.UnpSize<0)
+ Arc.FileHead.UnpSize=0;
+
+ wchar Command=Cmd->Command[0];
+ if (HeaderSize==0)
+ if (DataIO.UnpVolume)
+ {
+#ifdef NOVOLUME
+ return false;
+#else
+ // Supposing we unpack an old RAR volume without end of archive record
+ // and last file is not split between volumes.
+ if (!MergeArchive(Arc,&DataIO,false,Command))
+ {
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
+ }
+#endif
+ }
+ else
+ return false;
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType!=HEAD_FILE)
+ {
+#ifndef SFX_MODULE
+ if (HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
+ SetExtraInfo20(Cmd,Arc,DestFileName);
+#endif
+ if (HeaderType==HEAD_SERVICE && PrevProcessed)
+ SetExtraInfo(Cmd,Arc,DestFileName);
+ if (HeaderType==HEAD_ENDARC)
+ if (Arc.EndArcHead.NextVolume)
+ {
+#ifndef NOVOLUME
+ if (!MergeArchive(Arc,&DataIO,false,Command))
+ {
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
+ }
+#endif
+ Arc.Seek(Arc.CurBlockPos,SEEK_SET);
+ return true;
+ }
+ else
+ return false;
+ Arc.SeekToNext();
+ return true;
+ }
+ PrevProcessed=false;
+
+ if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
+ return false;
+
+ int MatchType=MATCH_WILDSUBPATH;
+
+ bool EqualNames=false;
+ wchar MatchedArg[NM];
+ int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,MatchedArg,ASIZE(MatchedArg));
+ bool MatchFound=MatchNumber!=0;
+#ifndef SFX_MODULE
+ if (Cmd->ExclPath==EXCL_BASEPATH)
+ {
+ wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
+ *PointToName(Cmd->ArcPath)=0;
+ if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
+ *Cmd->ArcPath=0;
+ }
+#endif
+ if (MatchFound && !EqualNames)
+ AllMatchesExact=false;
+
+ Arc.ConvertAttributes();
+
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (Arc.FileHead.SplitBefore && FirstFile)
+ {
+ wchar CurVolName[NM];
+ wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
+ VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering);
+
+ if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
+ {
+ // If first volume name does not match the current name and if such
+ // volume name really exists, let's unpack from this first volume.
+ Repeat=true;
+ return false;
+ }
+#ifndef RARDLL
+ if (!ReconstructDone)
+ {
+ ReconstructDone=true;
+ if (RecVolumesRestore(Cmd,Arc.FileName,true))
+ {
+ Repeat=true;
+ return false;
+ }
+ }
+#endif
+ wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
+ }
+#endif
+
+ wchar ArcFileName[NM];
+ ConvertPath(Arc.FileHead.FileName,ArcFileName);
+
+ if (Arc.FileHead.Version)
+ {
+ if (Cmd->VersionControl!=1 && !EqualNames)
+ {
+ if (Cmd->VersionControl==0)
+ MatchFound=false;
+ int Version=ParseVersionFileName(ArcFileName,false);
+ if (Cmd->VersionControl-1==Version)
+ ParseVersionFileName(ArcFileName,true);
+ else
+ MatchFound=false;
+ }
+ }
+ else
+ if (!Arc.IsArcDir() && Cmd->VersionControl>1)
+ MatchFound=false;
+
+ DataIO.UnpVolume=Arc.FileHead.SplitAfter;
+ DataIO.NextVolumeMissing=false;
+
+ Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
+
+ bool ExtrFile=false;
+ bool SkipSolid=false;
+
+#ifndef SFX_MODULE
+ if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
+ {
+ if (MatchFound)
+ {
+ uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_BAD_DATA;
+#endif
+ ErrHandler.SetErrorCode(RARX_OPEN);
+ }
+ MatchFound=false;
+ }
+
+ FirstFile=false;
+#endif
+
+ if (MatchFound || (SkipSolid=Arc.Solid)!=0)
+ {
+ // First common call of uiStartFileExtract. It is done before overwrite
+ // prompts, so if SkipSolid state is changed below, we'll need to make
+ // additional uiStartFileExtract calls with updated parameters.
+ if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
+ return false;
+
+ ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
+
+ // DestFileName can be set empty in case of excessive -ap switch.
+ ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
+
+ if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
+ {
+ FindData FD;
+ if (FindFile::FastFind(DestFileName,&FD))
+ {
+ if (FD.mtime >= Arc.FileHead.mtime)
+ {
+ // If directory already exists and its modification time is newer
+ // than start of extraction, it is likely it was created
+ // when creating a path to one of already extracted items.
+ // In such case we'll better update its time even if archived
+ // directory is older.
+
+ if (!FD.IsDir || FD.mtimeFreshFiles)
+ ExtrFile=false;
+ }
+
+ if (Arc.FileHead.Encrypted)
+ {
+ // Stop archive extracting if user cancelled a password prompt.
+#ifdef RARDLL
+ if (!ExtrDllGetPassword())
+ {
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ return false;
+ }
+#else
+ if (!ExtrGetPassword(Arc,ArcFileName))
+ {
+ PasswordCancelled=true;
+ return false;
+ }
+#endif
+ // Skip only the current encrypted file if empty password is entered.
+ // Actually our "cancel" code above intercepts empty passwords too now,
+ // so we keep the code below just in case we'll decide process empty
+ // and cancelled passwords differently sometimes.
+ if (!Cmd->Password.IsSet())
+ {
+ ErrHandler.SetErrorCode(RARX_WARNING);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+#endif
+ ExtrFile=false;
+ }
+ }
+
+#ifdef RARDLL
+ if (*Cmd->DllDestName!=0)
+ wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
+#endif
+
+ if (!CheckUnpVer(Arc,ArcFileName))
+ {
+ ErrHandler.SetErrorCode(RARX_FATAL);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_UNKNOWN_FORMAT;
+#endif
+ Arc.SeekToNext();
+ return !Arc.Solid; // Can try extracting next file only in non-solid archive.
+ }
+
+
+ // Set a password before creating the file, so we can skip creating
+ // in case of wrong password.
+ SecPassword FilePassword=Cmd->Password;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ConvertDosPassword(Arc,FilePassword);
+#endif
+
+ byte PswCheck[SIZE_PSWCHECK];
+ DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
+ Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
+ Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
+ Arc.FileHead.HashKey,PswCheck);
+
+ // If header is damaged, we cannot rely on password check value,
+ // because it can be damaged too.
+ if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
+ memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
+ !Arc.BrokenHeader)
+ {
+ uiMsg(UIERROR_BADPSW,Arc.FileName);
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ ExtrFile=false;
+#ifdef RARDLL
+ // If we already have ERAR_EOPEN as result of missing volume,
+ // we should not replace it with less precise ERAR_BAD_DATA.
+ if (Cmd->DllError!=ERAR_EOPEN)
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+#endif
+ }
+
+ File CurFile;
+
+ bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
+ if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
+ {
+ if (ExtrFile && Command!='P' && !Cmd->Test)
+ {
+ // Overwrite prompt for symbolic and hard links.
+ bool UserReject=false;
+ if (FileExist(DestFileName) && !UserReject)
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ if (UserReject)
+ ExtrFile=false;
+ }
+ }
+ else
+ if (Arc.IsArcDir())
+ {
+ if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ return true;
+ TotalFileCount++;
+ ExtrCreateDir(Arc,ArcFileName);
+ return true;
+ }
+ else
+ if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
+ ExtrFile=ExtrCreateFile(Arc,CurFile);
+
+ if (!ExtrFile && Arc.Solid)
+ {
+ SkipSolid=true;
+ ExtrFile=true;
+
+ // We changed SkipSolid, so we need to call uiStartFileExtract
+ // with "Skip" parameter to change the operation status
+ // from "extracting" to "skipping". For example, it can be necessary
+ // if user answered "No" to overwrite prompt when unpacking
+ // a solid archive.
+ if (!uiStartFileExtract(ArcFileName,false,false,true))
+ return false;
+ }
+ if (ExtrFile)
+ {
+ // Set it in test mode, so we also test subheaders such as NTFS streams
+ // after tested file.
+ if (Cmd->Test)
+ PrevProcessed=true;
+
+ bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.
+
+ if (!SkipSolid)
+ {
+ if (!TestMode && Command!='P' && CurFile.IsDevice())
+ {
+ uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
+ ErrHandler.WriteError(Arc.FileName,DestFileName);
+ }
+ TotalFileCount++;
+ }
+ FileCount++;
+#ifndef GUI
+ if (Command!='I')
+ if (SkipSolid)
+ mprintf(St(MExtrSkipFile),ArcFileName);
+ else
+ switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
+ {
+ case 'T':
+ mprintf(St(MExtrTestFile),ArcFileName);
+ break;
+#ifndef SFX_MODULE
+ case 'P':
+ mprintf(St(MExtrPrinting),ArcFileName);
+ break;
+#endif
+ case 'X':
+ case 'E':
+ mprintf(St(MExtrFile),DestFileName);
+ break;
+ }
+ if (!Cmd->DisablePercentage)
+ mprintf(L" ");
+#endif
+
+ DataIO.CurUnpRead=0;
+ DataIO.CurUnpWrite=0;
+ DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
+ DataIO.SetFiles(&Arc,&CurFile);
+ DataIO.SetTestMode(TestMode);
+ DataIO.SetSkipUnpCRC(SkipSolid);
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ if (!TestMode && !Arc.BrokenHeader &&
+ Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
+ {
+ if (!Fat32) // Not detected yet.
+ NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
+ if (Fat32)
+ uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
+ }
+#endif
+
+ if (!TestMode && !Arc.BrokenHeader &&
+ (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize &&
+ (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
+ CurFile.Prealloc(Arc.FileHead.UnpSize);
+
+ CurFile.SetAllowDelete(!Cmd->KeepBroken);
+
+ bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
+ bool ShowChecksum=true; // Display checksum verification result.
+
+ bool LinkSuccess=true; // Assume success for test mode.
+ if (LinkEntry)
+ {
+ FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;
+
+ if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
+ {
+ wchar NameExisting[NM];
+ ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting));
+ if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
+ if (Type==FSREDIR_HARDLINK)
+ LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting));
+ else
+ LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
+ }
+ else
+ if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
+ {
+ if (FileCreateMode)
+ LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
+ }
+ else
+ {
+ uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName);
+ LinkSuccess=false;
+ }
+
+ if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
+ {
+ // RAR 5.x links have a valid data checksum even in case of
+ // failure, because they do not store any data.
+ // We do not want to display "OK" in this case.
+ // For 4.x symlinks we verify the checksum only when extracting,
+ // but not when testing an archive.
+ ShowChecksum=false;
+ }
+ PrevProcessed=FileCreateMode && LinkSuccess;
+ }
+ else
+ if (!Arc.FileHead.SplitBefore)
+ if (Arc.FileHead.Method==0)
+ UnstoreFile(DataIO,Arc.FileHead.UnpSize);
+ else
+ {
+#ifdef _ANDROID
+ // malloc and new do not report memory allocation errors
+ // in Android, so if free memory is set, check it here
+ // trying to prevent crash.
+ if (Cmd->FreeMem!=0 && Cmd->FreeMem < Arc.FileHead.WinSize)
+ throw std::bad_alloc();
+#endif
+ Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
+ Unp->SetDestSize(Arc.FileHead.UnpSize);
+#ifndef SFX_MODULE
+ if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
+ Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
+ else
+#endif
+ Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
+ }
+
+ Arc.SeekToNext();
+
+ // We check for "split after" flag to detect partially extracted files
+ // from incomplete volume sets. For them file header contains packed
+ // data hash, which must not be compared against unpacked data hash
+ // to prevent accidental match. Moreover, for -m0 volumes packed data
+ // hash would match truncated unpacked data hash and lead to fake "OK"
+ // in incomplete volume set.
+ bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);
+
+ // We set AnySolidDataUnpackedWell to true if we found at least one
+ // valid non-zero solid file in preceding solid stream. If it is true
+ // and if current encrypted file is broken, we do not need to hint
+ // about a wrong password and can report CRC error only.
+ if (!Arc.FileHead.Solid)
+ AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
+ else
+ if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
+ AnySolidDataUnpackedWell=true;
+
+ bool BrokenFile=false;
+
+ // Checksum is not calculated in skip solid mode for performance reason.
+ if (!SkipSolid && ShowChecksum)
+ {
+ if (ValidCRC)
+ {
+#ifndef GUI
+ if (Command!='P' && Command!='I')
+ mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
+ Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk));
+#endif
+ }
+ else
+ {
+ if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
+ Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
+ uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
+ else
+ uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
+ BrokenFile=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+#ifdef RARDLL
+ // If we already have ERAR_EOPEN as result of missing volume
+ // or ERAR_BAD_PASSWORD for RAR5 wrong password,
+ // we should not replace it with less precise ERAR_BAD_DATA.
+ if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD)
+ Cmd->DllError=ERAR_BAD_DATA;
+#endif
+ }
+ }
+#ifndef GUI
+ else
+ mprintf(L"\b\b\b\b\b ");
+#endif
+
+ if (!TestMode && (Command=='X' || Command=='E') &&
+ (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
+ (!BrokenFile || Cmd->KeepBroken))
+ {
+ // We could preallocate more space that really written to broken file.
+ if (BrokenFile)
+ CurFile.Truncate();
+
+#if defined(_WIN_ALL) || defined(_EMX)
+ if (Cmd->ClearArc)
+ Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
+#endif
+
+
+ CurFile.SetOpenFileTime(
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
+ CurFile.Close();
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
+ SetFileCompression(CurFile.FileName,true);
+#endif
+#ifdef _UNIX
+ if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
+ SetUnixOwner(Arc,CurFile.FileName);
+#endif
+
+ CurFile.SetCloseFileTime(
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
+ if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr))
+ uiMsg(UIERROR_FILEATTR,Arc.FileName,CurFile.FileName);
+
+ PrevProcessed=true;
+ }
+ }
+ }
+ if (MatchFound)
+ MatchedArgs++;
+ if (DataIO.NextVolumeMissing)
+ return false;
+ if (!ExtrFile)
+ if (!Arc.Solid)
+ Arc.SeekToNext();
+ else
+ if (!SkipSolid)
+ return false;
+ return true;
+}
+
+
+void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
+{
+ // 512 KB and larger buffer reported to reduce performance on old XP
+ // computers with WDC WD2000JD HDD. According to test made by user
+ // 256 KB buffer is optimal.
+ Array Buffer(0x40000);
+ while (1)
+ {
+ uint Code=DataIO.UnpRead(&Buffer[0],Buffer.Size());
+ if (Code==0 || (int)Code==-1)
+ break;
+ Code=Code=0)
+ DestUnpSize-=Code;
+ }
+}
+
+
+bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+{
+ SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
+
+ File Existing;
+ if (!Existing.WOpen(NameExisting))
+ {
+ uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
+ uiMsg(UIERROR_FILECOPYHINT,ArcName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_EREFERENCE;
+#endif
+ return false;
+ }
+
+ Array Buffer(0x100000);
+ int64 CopySize=0;
+
+ while (true)
+ {
+ Wait();
+ int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
+ if (ReadSize==0)
+ break;
+ New.Write(&Buffer[0],ReadSize);
+ CopySize+=ReadSize;
+ }
+
+ return true;
+}
+
+
+void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
+{
+ wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
+
+ if (*Cmd->ExtrPath!=0)
+ {
+ wchar LastChar=*PointToLastChar(Cmd->ExtrPath);
+ // We need IsPathDiv check here to correctly handle Unix forward slash
+ // in the end of destination path in Windows: rar x arc dest/
+ // IsDriveDiv is needed for current drive dir: rar x arc d:
+ if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar))
+ {
+ // Destination path can be without trailing slash if it come from GUI shell.
+ AddEndSlash(DestName,DestSize);
+ }
+ }
+
+#ifndef SFX_MODULE
+ if (Cmd->AppendArcNameToPath)
+ {
+ wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
+ SetExt(DestName,NULL,DestSize);
+ AddEndSlash(DestName,DestSize);
+ }
+#endif
+
+#ifndef SFX_MODULE
+ size_t ArcPathLength=wcslen(Cmd->ArcPath);
+ if (ArcPathLength>0)
+ {
+ size_t NameLength=wcslen(ArcFileName);
+ ArcFileName+=Min(ArcPathLength,NameLength);
+ while (*ArcFileName==CPATHDIVIDER)
+ ArcFileName++;
+ if (*ArcFileName==0) // Excessive -ap switch.
+ {
+ *DestName=0;
+ return;
+ }
+ }
+#endif
+
+ wchar Command=Cmd->Command[0];
+ // Use -ep3 only in systems, where disk letters are exist, not in Unix.
+ bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
+
+ // We do not use any user specified destination paths when extracting
+ // absolute paths in -ep3 mode.
+ if (AbsPaths)
+ *DestName=0;
+
+ if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ wcsncatz(DestName,PointToName(ArcFileName),DestSize);
+ else
+ wcsncatz(DestName,ArcFileName,DestSize);
+
+ wchar DiskLetter=toupperw(DestName[0]);
+
+ if (AbsPaths)
+ {
+ if (DestName[1]=='_' && IsPathDiv(DestName[2]) &&
+ DiskLetter>='A' && DiskLetter<='Z')
+ DestName[1]=':';
+ else
+ if (DestName[0]=='_' && DestName[1]=='_')
+ {
+ // Convert __server\share to \\server\share.
+ DestName[0]=CPATHDIVIDER;
+ DestName[1]=CPATHDIVIDER;
+ }
+ }
+}
+
+
+#ifdef RARDLL
+bool CmdExtract::ExtrDllGetPassword()
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ Cmd->ManualPassword=true;
+ }
+ if (!Cmd->Password.IsSet())
+ return false;
+ }
+ return true;
+}
+#endif
+
+
+#ifndef RARDLL
+bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password) || !Cmd->Password.IsSet())
+ {
+ // Suppress "test is ok" message in GUI if user entered
+ // an empty password or cancelled a password prompt.
+ uiMsg(UIERROR_INCERRCOUNT);
+
+ return false;
+ }
+ Cmd->ManualPassword=true;
+ }
+#if !defined(GUI) && !defined(SILENT)
+ else
+ if (!PasswordAll && !Arc.FileHead.Solid)
+ {
+ eprintf(St(MUseCurPsw),ArcFileName);
+ switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll)))
+ {
+ case -1:
+ ErrHandler.Exit(RARX_USERBREAK);
+ case 2:
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password))
+ return false;
+ break;
+ case 3:
+ PasswordAll=true;
+ break;
+ }
+ }
+#endif
+ return true;
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd)
+{
+ if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS)
+ {
+ // We need the password in OEM encoding if file was encrypted by
+ // native RAR/DOS (not extender based). Let's make the conversion.
+ wchar PlainPsw[MAXPASSWORD];
+ Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw));
+ char PswA[MAXPASSWORD];
+ CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA));
+ PswA[ASIZE(PswA)-1]=0;
+ CharToWide(PswA,PlainPsw,ASIZE(PlainPsw));
+ DestPwd.Set(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ cleandata(PswA,sizeof(PswA));
+ }
+}
+#endif
+
+
+void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
+{
+ if (Cmd->Test)
+ {
+#ifndef GUI
+ mprintf(St(MExtrTestFile),ArcFileName);
+ mprintf(L" %s",St(MOk));
+#endif
+ return;
+ }
+
+ MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ bool DirExist=false;
+ if (MDCode!=MKDIR_SUCCESS)
+ {
+ DirExist=FileExist(DestFileName);
+ if (DirExist && !IsDir(GetFileAttr(DestFileName)))
+ {
+ // File with name same as this directory exists. Propose user
+ // to overwrite it.
+ bool UserReject;
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ DirExist=false;
+ }
+ if (!DirExist)
+ {
+ CreatePath(DestFileName,true);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ if (MDCode!=MKDIR_SUCCESS)
+ {
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+ MakeNameUsable(DestFileName,true);
+ CreatePath(DestFileName,true);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+#ifndef SFX_MODULE
+ if (MDCode==MKDIR_SUCCESS)
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ }
+ }
+ }
+ if (MDCode==MKDIR_SUCCESS)
+ {
+#ifndef GUI
+ mprintf(St(MCreatDir),DestFileName);
+ mprintf(L" %s",St(MOk));
+#endif
+ PrevProcessed=true;
+ }
+ else
+ if (DirExist)
+ {
+ if (!Cmd->IgnoreGeneralAttr)
+ SetFileAttr(DestFileName,Arc.FileHead.FileAttr);
+ PrevProcessed=true;
+ }
+ else
+ {
+ uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName);
+ ErrHandler.SysErrMsg();
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ if (PrevProcessed)
+ {
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
+ SetFileCompression(DestFileName,true);
+#endif
+ SetDirTime(DestFileName,
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
+ }
+}
+
+
+bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
+{
+ bool Success=true;
+ wchar Command=Cmd->Command[0];
+#if !defined(GUI) && !defined(SFX_MODULE)
+ if (Command=='P')
+ CurFile.SetHandleType(FILE_HANDLESTD);
+#endif
+ if ((Command=='E' || Command=='X') && !Cmd->Test)
+ {
+ bool UserReject;
+ // Specify "write only" mode to avoid OpenIndiana NAS problems
+ // with SetFileTime and read+write files.
+ if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+ Success=false;
+ if (!UserReject)
+ {
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ if (!IsNameUsable(DestFileName))
+ {
+ uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
+
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+
+ MakeNameUsable(DestFileName,true);
+
+ CreatePath(DestFileName,true);
+ if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+#ifndef SFX_MODULE
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ Success=true;
+ }
+ else
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+ }
+ }
+ }
+ }
+ return Success;
+}
+
+
+bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
+{
+ bool WrongVer;
+ if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives.
+ WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5;
+ else
+ {
+#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives.
+ WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK;
+#else // All formats since 1.3 for RAR.
+ WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK;
+#endif
+ }
+
+ // We can unpack stored files regardless of compression version field.
+ if (Arc.FileHead.Method==0)
+ WrongVer=false;
+
+ if (WrongVer)
+ {
+ ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
+ uiMsg(UIERROR_NEWERRAR,Arc.FileName);
+ }
+ return !WrongVer;
+}
diff --git a/extract.hpp b/extract.hpp
new file mode 100644
index 0000000..85a21f5
--- /dev/null
+++ b/extract.hpp
@@ -0,0 +1,62 @@
+#ifndef _RAR_EXTRACT_
+#define _RAR_EXTRACT_
+
+enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
+
+class CmdExtract
+{
+ private:
+ EXTRACT_ARC_CODE ExtractArchive();
+ bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+ void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
+#ifdef RARDLL
+ bool ExtrDllGetPassword();
+#else
+ bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
+#endif
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
+#endif
+ void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
+ bool ExtrCreateFile(Archive &Arc,File &CurFile);
+ bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
+
+ RarTime StartTime; // time when extraction started
+
+ CommandData *Cmd;
+
+ ComprDataIO DataIO;
+ Unpack *Unp;
+ unsigned long TotalFileCount;
+
+ unsigned long FileCount;
+ unsigned long MatchedArgs;
+ bool FirstFile;
+ bool AllMatchesExact;
+ bool ReconstructDone;
+
+ // If any non-zero solid file was successfully unpacked before current.
+ // If true and if current encrypted file is broken, obviously
+ // the password is correct and we can report broken CRC without
+ // any wrong password hints.
+ bool AnySolidDataUnpackedWell;
+
+ wchar ArcName[NM];
+
+ bool PasswordAll;
+ bool PrevProcessed; // If previous file was successfully extracted or tested.
+ wchar DestFileName[NM];
+ bool PasswordCancelled;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ bool Fat32,NotFat32;
+#endif
+ public:
+ CmdExtract(CommandData *Cmd);
+ ~CmdExtract();
+ void DoExtract();
+ void ExtractArchiveInit(Archive &Arc);
+ bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
+ static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
+};
+
+#endif
diff --git a/filcreat.cpp b/filcreat.cpp
new file mode 100644
index 0000000..d2bf0ac
--- /dev/null
+++ b/filcreat.cpp
@@ -0,0 +1,176 @@
+#include "rar.hpp"
+
+// If NewFile==NULL, we delete created file after user confirmation.
+// It is useful we we need to overwrite an existing folder or file,
+// but need user confirmation for that.
+bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
+{
+ if (UserReject!=NULL)
+ *UserReject=false;
+#ifdef _WIN_ALL
+ bool ShortNameChanged=false;
+#endif
+ while (FileExist(Name))
+ {
+#ifdef _WIN_ALL
+ if (!ShortNameChanged)
+ {
+ // Avoid the infinite loop if UpdateExistingShortName returns
+ // the same name.
+ ShortNameChanged=true;
+
+ // Maybe our long name matches the short name of existing file.
+ // Let's check if we can change the short name.
+ if (UpdateExistingShortName(Name))
+ continue;
+ }
+ // Allow short name check again. It is necessary, because rename and
+ // autorename below can change the name, so we need to check it again.
+ ShortNameChanged=false;
+#endif
+ UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
+
+ if (Choice==UIASKREP_R_REPLACE)
+ break;
+ if (Choice==UIASKREP_R_SKIP)
+ {
+ if (UserReject!=NULL)
+ *UserReject=true;
+ return false;
+ }
+ if (Choice==UIASKREP_R_CANCEL)
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+
+ // Try to truncate the existing file first instead of delete,
+ // so we preserve existing file permissions such as NTFS permissions.
+ uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
+ if (NewFile!=NULL && NewFile->Create(Name,FileMode))
+ return true;
+
+ CreatePath(Name,true);
+ return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
+}
+
+
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
+{
+ wchar NewName[NM];
+ size_t NameLength=wcslen(Name);
+#ifdef _ANDROID
+ if (NameLength>ASIZE(NewName)-10)
+ return false;
+#endif
+ wchar *Ext=GetExt(Name);
+ if (Ext==NULL)
+ Ext=Name+NameLength;
+ for (uint FileVer=1;;FileVer++)
+ {
+#ifdef _ANDROID // No swprintf in Android prior to Android 5.0.
+ uint NamePrefixLength=Ext-Name;
+ wcsncpy(NewName,Name,NamePrefixLength);
+ wcscpy(NewName+NamePrefixLength,L"(");
+ itoa(FileVer,NewName+NamePrefixLength+1,ASIZE(NewName)-NamePrefixLength-1);
+ wcsncatz(NewName,L")",ASIZE(NewName));
+ wcsncatz(NewName,Ext,ASIZE(NewName));
+#else
+ swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
+#endif
+ if (!FileExist(NewName))
+ {
+ wcsncpyz(Name,NewName,MaxNameSize);
+ break;
+ }
+ if (FileVer>=1000000)
+ return false;
+ }
+ return true;
+}
+
+
+#ifdef _WIN_ALL
+// If we find a file, which short name is equal to 'Name', we try to change
+// its short name, while preserving the long name. It helps when unpacking
+// an archived file, which long name is equal to short name of already
+// existing file. Otherwise we would overwrite the already existing file,
+// even though its long name does not match the name of unpacking file.
+bool UpdateExistingShortName(const wchar *Name)
+{
+ wchar LongPathName[NM];
+ DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
+ if (Res==0 || Res>=ASIZE(LongPathName))
+ return false;
+ wchar ShortPathName[NM];
+ Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
+ if (Res==0 || Res>=ASIZE(ShortPathName))
+ return false;
+ wchar *LongName=PointToName(LongPathName);
+ wchar *ShortName=PointToName(ShortPathName);
+
+ // We continue only if file has a short name, which does not match its
+ // long name, and this short name is equal to name of file which we need
+ // to create.
+ if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
+ wcsicomp(PointToName(Name),ShortName)!=0)
+ return false;
+
+ // Generate the temporary new name for existing file.
+ wchar NewName[NM];
+ *NewName=0;
+ for (int I=0;I<10000 && *NewName==0;I+=123)
+ {
+ // Here we copy the path part of file to create. We'll make the temporary
+ // file in the same folder.
+ wcsncpyz(NewName,Name,ASIZE(NewName));
+
+ // Here we set the random name part.
+ swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
+
+ // If such file is already exist, try next random name.
+ if (FileExist(NewName))
+ *NewName=0;
+ }
+
+ // If we could not generate the name not used by any other file, we return.
+ if (*NewName==0)
+ return false;
+
+ // FastFind returns the name without path, but we need the fully qualified
+ // name for renaming, so we use the path from file to create and long name
+ // from existing file.
+ wchar FullName[NM];
+ wcsncpyz(FullName,Name,ASIZE(FullName));
+ SetName(FullName,LongName,ASIZE(FullName));
+
+ // Rename the existing file to randomly generated name. Normally it changes
+ // the short name too.
+ if (!MoveFile(FullName,NewName))
+ return false;
+
+ // Now we need to create the temporary empty file with same name as
+ // short name of our already existing file. We do it to occupy its previous
+ // short name and not allow to use it again when renaming the file back to
+ // its original long name.
+ File KeepShortFile;
+ bool Created=false;
+ if (!FileExist(Name))
+ Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
+
+ // Now we rename the existing file from temporary name to original long name.
+ // Since its previous short name is occupied by another file, it should
+ // get another short name.
+ MoveFile(NewName,FullName);
+
+ if (Created)
+ {
+ // Delete the temporary zero length file occupying the short name,
+ KeepShortFile.Close();
+ KeepShortFile.Delete();
+ }
+ // We successfully changed the short name. Maybe sometimes we'll simplify
+ // this function by use of SetFileShortName Windows API call.
+ // But SetFileShortName is not available in older Windows.
+ return true;
+}
+#endif
diff --git a/filcreat.hpp b/filcreat.hpp
new file mode 100644
index 0000000..e8368cd
--- /dev/null
+++ b/filcreat.hpp
@@ -0,0 +1,14 @@
+#ifndef _RAR_FILECREATE_
+#define _RAR_FILECREATE_
+
+bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize=INT64NDF,
+ RarTime *FileTime=NULL,bool WriteOnly=false);
+
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
+
+#ifdef _WIN_ALL
+bool UpdateExistingShortName(const wchar *Name);
+#endif
+
+#endif
diff --git a/file.cpp b/file.cpp
new file mode 100644
index 0000000..044eddf
--- /dev/null
+++ b/file.cpp
@@ -0,0 +1,725 @@
+#include "rar.hpp"
+
+File::File()
+{
+ hFile=FILE_BAD_HANDLE;
+ *FileName=0;
+ NewFile=false;
+ LastWrite=false;
+ HandleType=FILE_HANDLENORMAL;
+ SkipClose=false;
+ IgnoreReadErrors=false;
+ ErrorType=FILE_SUCCESS;
+ OpenShared=false;
+ AllowDelete=true;
+ AllowExceptions=true;
+#ifdef _WIN_ALL
+ NoSequentialRead=false;
+ CreateMode=FMF_UNDEFINED;
+#endif
+}
+
+
+File::~File()
+{
+ if (hFile!=FILE_BAD_HANDLE && !SkipClose)
+ if (NewFile)
+ Delete();
+ else
+ Close();
+}
+
+
+void File::operator = (File &SrcFile)
+{
+ hFile=SrcFile.hFile;
+ NewFile=SrcFile.NewFile;
+ LastWrite=SrcFile.LastWrite;
+ HandleType=SrcFile.HandleType;
+ wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
+ SrcFile.SkipClose=true;
+}
+
+
+bool File::Open(const wchar *Name,uint Mode)
+{
+ ErrorType=FILE_SUCCESS;
+ FileHandle hNewFile;
+ bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
+ bool UpdateMode=(Mode & FMF_UPDATE)!=0;
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+#ifdef _WIN_ALL
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
+ if (UpdateMode)
+ Access|=GENERIC_WRITE;
+ uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
+ if (OpenShared)
+ ShareMode|=FILE_SHARE_WRITE;
+ uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
+ hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+
+ DWORD LastError;
+ if (hNewFile==FILE_BAD_HANDLE)
+ {
+ LastError=GetLastError();
+
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ {
+ hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+
+ // For archive names longer than 260 characters first CreateFile
+ // (without \\?\) fails and sets LastError to 3 (access denied).
+ // We need the correct "file not found" error code to decide
+ // if we create a new archive or quit with "cannot create" error.
+ // So we need to check the error code after \\?\ CreateFile again,
+ // otherwise we'll fail to create new archives with long names.
+ // But we cannot simply assign the new code to LastError,
+ // because it would break "..\arcname.rar" relative names processing.
+ // First CreateFile returns the correct "file not found" code for such
+ // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
+ // dots as a directory name. So we check only for "file not found"
+ // error here and for other errors use the first CreateFile result.
+ if (GetLastError()==ERROR_FILE_NOT_FOUND)
+ LastError=ERROR_FILE_NOT_FOUND;
+ }
+ }
+
+ if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
+ ErrorType=FILE_NOTFOUND;
+#else
+ int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
+#ifdef O_BINARY
+ flags|=O_BINARY;
+#if defined(_AIX) && defined(_LARGE_FILE_API)
+ flags|=O_LARGEFILE;
+#endif
+#endif
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+
+ int handle=open(NameA,flags);
+#ifdef LOCK_EX
+
+#ifdef _OSF_SOURCE
+ extern "C" int flock(int, int);
+#endif
+
+ if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
+ {
+ close(handle);
+ return false;
+ }
+#endif
+ if (handle==-1)
+ hNewFile=FILE_BAD_HANDLE;
+ else
+ {
+#ifdef FILE_USE_OPEN
+ hNewFile=handle;
+#else
+ hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
+#endif
+ }
+#ifdef _ANDROID
+ // If we open an existing file in r&w mode and external card is read-only
+ // for usual file API.
+ if (hNewFile==FILE_BAD_HANDLE && UpdateMode && errno!=ENOENT)
+ hNewFile=JniOpenFile(Name);
+#endif
+ if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
+ ErrorType=FILE_NOTFOUND;
+#endif
+ NewFile=false;
+ HandleType=FILE_HANDLENORMAL;
+ SkipClose=false;
+ bool Success=hNewFile!=FILE_BAD_HANDLE;
+ if (Success)
+ {
+ hFile=hNewFile;
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+ }
+ return Success;
+}
+
+
+#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
+void File::TOpen(const wchar *Name)
+{
+ if (!WOpen(Name))
+ ErrHandler.Exit(RARX_OPEN);
+}
+#endif
+
+
+bool File::WOpen(const wchar *Name)
+{
+ if (Open(Name))
+ return true;
+ ErrHandler.OpenErrorMsg(Name);
+ return false;
+}
+
+
+bool File::Create(const wchar *Name,uint Mode)
+{
+ // OpenIndiana based NAS and CIFS shares fail to set the file time if file
+ // was created in read+write mode and some data was written and not flushed
+ // before SetFileTime call. So we should use the write only mode if we plan
+ // SetFileTime call and do not need to read from file.
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+ bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
+#ifdef _WIN_ALL
+ CreateMode=Mode;
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
+ DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
+
+ // Windows automatically removes dots and spaces in the end of file name,
+ // So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+
+ if (Special)
+ hFile=FILE_BAD_HANDLE;
+ else
+ hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+
+ if (hFile==FILE_BAD_HANDLE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+ }
+
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+#ifdef FILE_USE_OPEN
+ hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR));
+#ifdef _ANDROID
+ if (hFile==FILE_BAD_HANDLE)
+ hFile=JniCreateFile(Name); // If external card is read-only for usual file API.
+ if (hFile!=FILE_BAD_HANDLE)
+ JniFileNotify(Name,false);
+#endif
+#else
+ hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
+#endif
+#endif
+ NewFile=true;
+ HandleType=FILE_HANDLENORMAL;
+ SkipClose=false;
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+ return hFile!=FILE_BAD_HANDLE;
+}
+
+
+#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
+void File::TCreate(const wchar *Name,uint Mode)
+{
+ if (!WCreate(Name,Mode))
+ ErrHandler.Exit(RARX_FATAL);
+}
+#endif
+
+
+bool File::WCreate(const wchar *Name,uint Mode)
+{
+ if (Create(Name,Mode))
+ return true;
+ ErrHandler.CreateErrorMsg(Name);
+ return false;
+}
+
+
+bool File::Close()
+{
+ bool Success=true;
+
+ if (hFile!=FILE_BAD_HANDLE)
+ {
+ if (!SkipClose)
+ {
+#ifdef _WIN_ALL
+ // We use the standard system handle for stdout in Windows
+ // and it must not be closed here.
+ if (HandleType==FILE_HANDLENORMAL)
+ Success=CloseHandle(hFile)==TRUE;
+#else
+#ifdef FILE_USE_OPEN
+ Success=close(hFile)!=-1;
+#else
+ Success=fclose(hFile)!=EOF;
+#endif
+#endif
+ }
+ hFile=FILE_BAD_HANDLE;
+ }
+ HandleType=FILE_HANDLENORMAL;
+ if (!Success && AllowExceptions)
+ ErrHandler.CloseError(FileName);
+ return Success;
+}
+
+
+bool File::Delete()
+{
+ if (HandleType!=FILE_HANDLENORMAL)
+ return false;
+ if (hFile!=FILE_BAD_HANDLE)
+ Close();
+ if (!AllowDelete)
+ return false;
+ return DelFile(FileName);
+}
+
+
+bool File::Rename(const wchar *NewName)
+{
+ // No need to rename if names are already same.
+ bool Success=wcscmp(FileName,NewName)==0;
+
+ if (!Success)
+ Success=RenameFile(FileName,NewName);
+
+ if (Success)
+ wcscpy(FileName,NewName);
+
+ return Success;
+}
+
+
+bool File::Write(const void *Data,size_t Size)
+{
+ if (Size==0)
+ return true;
+ if (HandleType==FILE_HANDLESTD)
+ {
+#ifdef _WIN_ALL
+ hFile=GetStdHandle(STD_OUTPUT_HANDLE);
+#else
+ // Cannot use the standard stdout here, because it already has wide orientation.
+ if (hFile==FILE_BAD_HANDLE)
+ {
+#ifdef FILE_USE_OPEN
+ hFile=dup(STDOUT_FILENO); // Open new stdout stream.
+#else
+ hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
+#endif
+ }
+#endif
+ }
+ bool Success;
+ while (1)
+ {
+ Success=false;
+#ifdef _WIN_ALL
+ DWORD Written=0;
+ if (HandleType!=FILE_HANDLENORMAL)
+ {
+ // writing to stdout can fail in old Windows if data block is too large
+ const size_t MaxSize=0x4000;
+ for (size_t I=0;ISize && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
+ ErrHandler.WriteErrorFAT(FileName);
+#endif
+ if (ErrHandler.AskRepeatWrite(FileName,false))
+ {
+#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
+ clearerr(hFile);
+#endif
+ if (Written0)
+ Seek(Tell()-Written,SEEK_SET);
+ continue;
+ }
+ ErrHandler.WriteError(NULL,FileName);
+ }
+ break;
+ }
+ LastWrite=true;
+ return Success; // It can return false only if AllowExceptions is disabled.
+}
+
+
+int File::Read(void *Data,size_t Size)
+{
+ int64 FilePos=0; // Initialized only to suppress some compilers warning.
+
+ if (IgnoreReadErrors)
+ FilePos=Tell();
+ int ReadSize;
+ while (true)
+ {
+ ReadSize=DirectRead(Data,Size);
+ if (ReadSize==-1)
+ {
+ ErrorType=FILE_READERROR;
+ if (AllowExceptions)
+ if (IgnoreReadErrors)
+ {
+ ReadSize=0;
+ for (size_t I=0;IMaxDeviceRead)
+// Size=MaxDeviceRead;
+ hFile=GetStdHandle(STD_INPUT_HANDLE);
+#else
+#ifdef FILE_USE_OPEN
+ hFile=STDIN_FILENO;
+#else
+ hFile=stdin;
+#endif
+#endif
+ }
+#ifdef _WIN_ALL
+ // For pipes like 'type file.txt | rar -si arcname' ReadFile may return
+ // data in small ~4KB blocks. It may slightly reduce the compression ratio.
+ DWORD Read;
+ if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
+ {
+ if (IsDevice() && Size>MaxDeviceRead)
+ return DirectRead(Data,MaxDeviceRead);
+ if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
+ return 0;
+
+ // We had a bug report about failure to archive 1C database lock file
+ // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
+ // permanently locked. If our first read request uses too large buffer
+ // and if we are in -dh mode, so we were able to open the file,
+ // we'll fail with "Read error". So now we use try a smaller buffer size
+ // in case of lock error.
+ if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
+ GetLastError()==ERROR_LOCK_VIOLATION)
+ return DirectRead(Data,MaxLockedRead);
+
+ return -1;
+ }
+ return Read;
+#else
+#ifdef FILE_USE_OPEN
+ ssize_t ReadSize=read(hFile,Data,Size);
+ if (ReadSize==-1)
+ return -1;
+ return (int)ReadSize;
+#else
+ if (LastWrite)
+ {
+ fflush(hFile);
+ LastWrite=false;
+ }
+ clearerr(hFile);
+ size_t ReadSize=fread(Data,1,Size,hFile);
+ if (ferror(hFile))
+ return -1;
+ return (int)ReadSize;
+#endif
+#endif
+}
+
+
+void File::Seek(int64 Offset,int Method)
+{
+ if (!RawSeek(Offset,Method) && AllowExceptions)
+ ErrHandler.SeekError(FileName);
+}
+
+
+bool File::RawSeek(int64 Offset,int Method)
+{
+ if (hFile==FILE_BAD_HANDLE)
+ return true;
+ if (Offset<0 && Method!=SEEK_SET)
+ {
+ Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
+ Method=SEEK_SET;
+ }
+#ifdef _WIN_ALL
+ LONG HighDist=(LONG)(Offset>>32);
+ if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
+ GetLastError()!=NO_ERROR)
+ return false;
+#else
+ LastWrite=false;
+#ifdef FILE_USE_OPEN
+ if (lseek64(hFile,Offset,Method)==-1)
+ return false;
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
+ if (fseeko(hFile,Offset,Method)!=0)
+ return false;
+#else
+ if (fseek(hFile,(long)Offset,Method)!=0)
+ return false;
+#endif
+#endif
+ return true;
+}
+
+
+int64 File::Tell()
+{
+ if (hFile==FILE_BAD_HANDLE)
+ if (AllowExceptions)
+ ErrHandler.SeekError(FileName);
+ else
+ return -1;
+#ifdef _WIN_ALL
+ LONG HighDist=0;
+ uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
+ if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
+ if (AllowExceptions)
+ ErrHandler.SeekError(FileName);
+ else
+ return -1;
+ return INT32TO64(HighDist,LowDist);
+#else
+#ifdef FILE_USE_OPEN
+ return lseek64(hFile,0,SEEK_CUR);
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
+ return ftello(hFile);
+#else
+ return ftell(hFile);
+#endif
+#endif
+}
+
+
+void File::Prealloc(int64 Size)
+{
+#ifdef _WIN_ALL
+ if (RawSeek(Size,SEEK_SET))
+ {
+ Truncate();
+ Seek(0,SEEK_SET);
+ }
+#endif
+
+#if defined(_UNIX) && defined(USE_FALLOCATE)
+ // fallocate is rather new call. Only latest kernels support it.
+ // So we are not using it by default yet.
+ int fd = GetFD(hFile);
+ if (fd >= 0)
+ fallocate(fd, 0, 0, Size);
+#endif
+}
+
+
+byte File::GetByte()
+{
+ byte Byte=0;
+ Read(&Byte,1);
+ return Byte;
+}
+
+
+void File::PutByte(byte Byte)
+{
+ Write(&Byte,1);
+}
+
+
+bool File::Truncate()
+{
+#ifdef _WIN_ALL
+ return SetEndOfFile(hFile)==TRUE;
+#else
+ return ftruncate(GetFD(),(off_t)Tell())==0;
+#endif
+}
+
+
+void File::Flush()
+{
+#ifdef _WIN_ALL
+ FlushFileBuffers(hFile);
+#else
+#ifndef FILE_USE_OPEN
+ fflush(hFile);
+#endif
+ fsync(GetFD());
+#endif
+}
+
+
+void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
+{
+#ifdef _WIN_ALL
+ // Workaround for OpenIndiana NAS time bug. If we cannot create a file
+ // in write only mode, we need to flush the write buffer before calling
+ // SetFileTime or file time will not be changed.
+ if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
+ FlushFileBuffers(hFile);
+
+ bool sm=ftm!=NULL && ftm->IsSet();
+ bool sc=ftc!=NULL && ftc->IsSet();
+ bool sa=fta!=NULL && fta->IsSet();
+ FILETIME fm,fc,fa;
+ if (sm)
+ ftm->GetWin32(&fm);
+ if (sc)
+ ftc->GetWin32(&fc);
+ if (sa)
+ fta->GetWin32(&fa);
+ SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
+#endif
+}
+
+
+void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
+{
+#ifdef _UNIX
+ SetCloseFileTimeByName(FileName,ftm,fta);
+#endif
+}
+
+
+void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
+{
+#ifdef _UNIX
+ bool setm=ftm!=NULL && ftm->IsSet();
+ bool seta=fta!=NULL && fta->IsSet();
+ if (setm || seta)
+ {
+ utimbuf ut;
+ if (setm)
+ ut.modtime=ftm->GetUnix();
+ else
+ ut.modtime=fta->GetUnix();
+ if (seta)
+ ut.actime=fta->GetUnix();
+ else
+ ut.actime=ut.modtime;
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ utime(NameA,&ut);
+ }
+#endif
+}
+
+
+void File::GetOpenFileTime(RarTime *ft)
+{
+#ifdef _WIN_ALL
+ FILETIME FileTime;
+ GetFileTime(hFile,NULL,NULL,&FileTime);
+ *ft=FileTime;
+#endif
+#if defined(_UNIX) || defined(_EMX)
+ struct stat st;
+ fstat(GetFD(),&st);
+ *ft=st.st_mtime;
+#endif
+}
+
+
+int64 File::FileLength()
+{
+ SaveFilePos SavePos(*this);
+ Seek(0,SEEK_END);
+ return Tell();
+}
+
+
+bool File::IsDevice()
+{
+ if (hFile==FILE_BAD_HANDLE)
+ return false;
+#ifdef _WIN_ALL
+ uint Type=GetFileType(hFile);
+ return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
+#else
+ return isatty(GetFD());
+#endif
+}
+
+
+#ifndef SFX_MODULE
+int64 File::Copy(File &Dest,int64 Length)
+{
+ Array Buffer(0x40000);
+ int64 CopySize=0;
+ bool CopyAll=(Length==INT64NDF);
+
+ while (CopyAll || Length>0)
+ {
+ Wait();
+ size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
+ char *Buf=&Buffer[0];
+ int ReadSize=Read(Buf,SizeToRead);
+ if (ReadSize==0)
+ break;
+ size_t WriteSize=ReadSize;
+#ifdef _WIN_ALL
+ // For FAT32 USB flash drives in Windows if first write is 4 KB or more,
+ // write caching is disabled and "write through" is enabled, resulting
+ // in bad performance, especially for many small files. It happens when
+ // we create SFX archive on USB drive, because SFX module is written first.
+ // So we split the first write to small 1 KB followed by rest of data.
+ if (CopySize==0 && WriteSize>=4096)
+ {
+ const size_t FirstWrite=1024;
+ Dest.Write(Buf,FirstWrite);
+ Buf+=FirstWrite;
+ WriteSize-=FirstWrite;
+ }
+#endif
+ Dest.Write(Buf,WriteSize);
+ CopySize+=ReadSize;
+ if (!CopyAll)
+ Length-=ReadSize;
+ }
+ return CopySize;
+}
+#endif
diff --git a/file.hpp b/file.hpp
new file mode 100644
index 0000000..139a40c
--- /dev/null
+++ b/file.hpp
@@ -0,0 +1,125 @@
+#ifndef _RAR_FILE_
+#define _RAR_FILE_
+
+#ifdef _ANDROID // Need lseek64 to handle >2 GB files in Android.
+#define FILE_USE_OPEN
+#endif
+
+#ifdef _WIN_ALL
+ typedef HANDLE FileHandle;
+ #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
+#elif defined(FILE_USE_OPEN)
+ typedef off_t FileHandle;
+ #define FILE_BAD_HANDLE -1
+#else
+ typedef FILE* FileHandle;
+ #define FILE_BAD_HANDLE NULL
+#endif
+
+class RAROptions;
+
+enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
+
+enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
+
+enum FILE_MODE_FLAGS {
+ // Request read only access to file. Default for Open.
+ FMF_READ=0,
+
+ // Request both read and write access to file. Default for Create.
+ FMF_UPDATE=1,
+
+ // Request write only access to file.
+ FMF_WRITE=2,
+
+ // Open files which are already opened for write by other programs.
+ FMF_OPENSHARED=4,
+
+ // Open files only if no other program is opened it even in shared mode.
+ FMF_OPENEXCLUSIVE=8,
+
+ // Provide read access to created file for other programs.
+ FMF_SHAREREAD=16,
+
+ // Mode flags are not defined yet.
+ FMF_UNDEFINED=256
+};
+
+
+class File
+{
+ private:
+ FileHandle hFile;
+ bool LastWrite;
+ FILE_HANDLETYPE HandleType;
+ bool SkipClose;
+ bool IgnoreReadErrors;
+ bool NewFile;
+ bool AllowDelete;
+ bool AllowExceptions;
+#ifdef _WIN_ALL
+ bool NoSequentialRead;
+ uint CreateMode;
+#endif
+ protected:
+ bool OpenShared; // Set by 'Archive' class.
+ public:
+ wchar FileName[NM];
+
+ FILE_ERRORTYPE ErrorType;
+ public:
+ File();
+ virtual ~File();
+ void operator = (File &SrcFile);
+ virtual bool Open(const wchar *Name,uint Mode=FMF_READ);
+ void TOpen(const wchar *Name);
+ bool WOpen(const wchar *Name);
+ bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ bool Close();
+ bool Delete();
+ bool Rename(const wchar *NewName);
+ bool Write(const void *Data,size_t Size);
+ virtual int Read(void *Data,size_t Size);
+ int DirectRead(void *Data,size_t Size);
+ virtual void Seek(int64 Offset,int Method);
+ bool RawSeek(int64 Offset,int Method);
+ virtual int64 Tell();
+ void Prealloc(int64 Size);
+ byte GetByte();
+ void PutByte(byte Byte);
+ bool Truncate();
+ void Flush();
+ void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
+ void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
+ static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
+ void GetOpenFileTime(RarTime *ft);
+ bool IsOpened() {return hFile!=FILE_BAD_HANDLE;};
+ int64 FileLength();
+ void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
+ FILE_HANDLETYPE GetHandleType() {return HandleType;}
+ bool IsDevice();
+ static bool RemoveCreated();
+ FileHandle GetHandle() {return hFile;}
+ void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
+ void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}
+ int64 Copy(File &Dest,int64 Length=INT64NDF);
+ void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
+ void SetExceptions(bool Allow) {AllowExceptions=Allow;}
+#ifdef _WIN_ALL
+ void RemoveSequentialFlag() {NoSequentialRead=true;}
+#endif
+#ifdef _UNIX
+ int GetFD()
+ {
+#ifdef FILE_USE_OPEN
+ return hFile;
+#else
+ return fileno(hFile);
+#endif
+ }
+#endif
+};
+
+#endif
diff --git a/filefn.cpp b/filefn.cpp
new file mode 100644
index 0000000..c344d3d
--- /dev/null
+++ b/filefn.cpp
@@ -0,0 +1,497 @@
+#include "rar.hpp"
+
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
+{
+#ifdef _WIN_ALL
+ // Windows automatically removes dots and spaces in the end of directory
+ // name. So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+ BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
+ if (RetCode==0 && !FileExist(Name))
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ RetCode=CreateDirectory(LongName,NULL);
+ }
+ if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
+ {
+ if (SetAttr)
+ SetFileAttr(Name,Attr);
+ return MKDIR_SUCCESS;
+ }
+ int ErrCode=GetLastError();
+ if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
+ return MKDIR_BADPATH;
+ return MKDIR_ERROR;
+#elif defined(_UNIX)
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ mode_t uattr=SetAttr ? (mode_t)Attr:0777;
+ int ErrCode=mkdir(NameA,uattr);
+#ifdef _ANDROID
+ if (ErrCode==-1 && errno!=ENOENT)
+ ErrCode=JniMkdir(Name) ? 0 : -1; // If external card is read-only for usual file API.
+ if (ErrCode!=-1)
+ JniFileNotify(Name,false);
+#endif
+ if (ErrCode==-1)
+ return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
+ return MKDIR_SUCCESS;
+#else
+ return MKDIR_ERROR;
+#endif
+}
+
+
+bool CreatePath(const wchar *Path,bool SkipLastName)
+{
+ if (Path==NULL || *Path==0)
+ return false;
+
+#if defined(_WIN_ALL) || defined(_EMX)
+ uint DirAttr=0;
+#else
+ uint DirAttr=0777;
+#endif
+
+ bool Success=true;
+
+ for (const wchar *s=Path;*s!=0;s++)
+ {
+ wchar DirName[NM];
+ if (s-Path>=ASIZE(DirName))
+ break;
+
+ // Process all kinds of path separators, so user can enter Unix style
+ // path in Windows or Windows in Unix. s>Path check avoids attempting
+ // creating an empty directory for paths starting from path separator.
+ if (IsPathDiv(*s) && s>Path)
+ {
+#ifdef _WIN_ALL
+ // We must not attempt to create "D:" directory, because first
+ // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine
+ // to create "D:" directory.
+ if (s==Path+2 && Path[1]==':')
+ continue;
+#endif
+ wcsncpy(DirName,Path,s-Path);
+ DirName[s-Path]=0;
+
+ Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS;
+#ifndef GUI
+ if (Success)
+ {
+ mprintf(St(MCreatDir),DirName);
+ mprintf(L" %s",St(MOk));
+ }
+#endif
+ }
+ }
+ if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path)))
+ Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS;
+ return Success;
+}
+
+
+void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta)
+{
+#ifdef _WIN_ALL
+ bool sm=ftm!=NULL && ftm->IsSet();
+ bool sc=ftc!=NULL && ftc->IsSet();
+ bool sa=fta!=NULL && fta->IsSet();
+
+ uint DirAttr=GetFileAttr(Name);
+ bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
+ if (ResetAttr)
+ SetFileAttr(Name,0);
+
+ HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (hFile==INVALID_HANDLE_VALUE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ }
+
+ if (hFile==INVALID_HANDLE_VALUE)
+ return;
+ FILETIME fm,fc,fa;
+ if (sm)
+ ftm->GetWin32(&fm);
+ if (sc)
+ ftc->GetWin32(&fc);
+ if (sa)
+ fta->GetWin32(&fa);
+ SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
+ CloseHandle(hFile);
+ if (ResetAttr)
+ SetFileAttr(Name,DirAttr);
+#endif
+#if defined(_UNIX) || defined(_EMX)
+ File::SetCloseFileTimeByName(Name,ftm,fta);
+#endif
+}
+
+
+bool IsRemovable(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ int Type=GetDriveType(*Root!=0 ? Root:NULL);
+ return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
+#else
+ return false;
+#endif
+}
+
+
+#ifndef SFX_MODULE
+int64 GetFreeDisk(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ wchar Root[NM];
+ GetFilePath(Name,Root,ASIZE(Root));
+
+ ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
+ uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
+ if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
+ uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
+ return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
+ return 0;
+#elif defined(_UNIX)
+ wchar Root[NM];
+ GetFilePath(Name,Root,ASIZE(Root));
+ char RootA[NM];
+ WideToChar(Root,RootA,ASIZE(RootA));
+ struct statvfs sfs;
+ if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
+ return 0;
+ int64 FreeSize=sfs.f_bsize;
+ FreeSize=FreeSize*sfs.f_bavail;
+ return FreeSize;
+#else
+ return 0;
+#endif
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
+// file size to 4 GB for these file systems.
+bool IsFAT(const wchar *Name)
+{
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ wchar FileSystem[MAX_PATH+1];
+ if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
+ return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
+ return false;
+}
+#endif
+
+
+bool FileExist(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ return GetFileAttr(Name)!=0xffffffff;
+#elif defined(ENABLE_ACCESS)
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return access(NameA,0)==0;
+#else
+ FindData FD;
+ return FindFile::FastFind(Name,&FD);
+#endif
+}
+
+
+bool WildFileExist(const wchar *Name)
+{
+ if (IsWildcard(Name))
+ {
+ FindFile Find;
+ Find.SetMask(Name);
+ FindData fd;
+ return Find.Next(&fd);
+ }
+ return FileExist(Name);
+}
+
+
+bool IsDir(uint Attr)
+{
+#ifdef _WIN_ALL
+ return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
+#endif
+#if defined(_UNIX)
+ return (Attr & 0xF000)==0x4000;
+#endif
+}
+
+
+bool IsUnreadable(uint Attr)
+{
+#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
+ return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
+#endif
+ return false;
+}
+
+
+bool IsLink(uint Attr)
+{
+#ifdef _UNIX
+ return (Attr & 0xF000)==0xA000;
+#elif defined(_WIN_ALL)
+ return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
+#else
+ return false;
+#endif
+}
+
+
+
+
+
+
+bool IsDeleteAllowed(uint FileAttr)
+{
+#ifdef _WIN_ALL
+ return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
+#else
+ return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
+#endif
+}
+
+
+void PrepareToDelete(const wchar *Name)
+{
+#if defined(_WIN_ALL) || defined(_EMX)
+ SetFileAttr(Name,0);
+#endif
+#ifdef _UNIX
+ if (Name!=NULL)
+ {
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
+ }
+#endif
+}
+
+
+uint GetFileAttr(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ DWORD Attr=GetFileAttributes(Name);
+ if (Attr==0xffffffff)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Attr=GetFileAttributes(LongName);
+ }
+ return Attr;
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ struct stat st;
+ if (stat(NameA,&st)!=0)
+ return 0;
+ return st.st_mode;
+#endif
+}
+
+
+bool SetFileAttr(const wchar *Name,uint Attr)
+{
+#ifdef _WIN_ALL
+ bool Success=SetFileAttributes(Name,Attr)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=SetFileAttributes(LongName,Attr)!=0;
+ }
+ return Success;
+#elif defined(_UNIX)
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return chmod(NameA,(mode_t)Attr)==0;
+#else
+ return false;
+#endif
+}
+
+
+
+
+#if !defined(SFX_MODULE) && !defined(SHELL_EXT) && !defined(SETUP)
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
+{
+ SaveFilePos SavePos(*SrcFile);
+#ifndef SILENT
+ int64 FileLength=SrcFile->FileLength();
+#endif
+
+#ifndef GUI
+ if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWALL))!=0)
+#endif
+ uiMsg(UIEVENT_FILESUMSTART);
+
+ if ((Flags & CALCFSUM_CURPOS)==0)
+ SrcFile->Seek(0,SEEK_SET);
+
+ const size_t BufSize=0x100000;
+ Array Data(BufSize);
+
+
+ DataHash HashCRC,HashBlake2;
+ HashCRC.Init(HASH_CRC32,Threads);
+ HashBlake2.Init(HASH_BLAKE2,Threads);
+
+ int64 BlockCount=0;
+ while (true)
+ {
+ size_t SizeToRead;
+ if (Size==INT64NDF) // If we process the entire file.
+ SizeToRead=BufSize; // Then always attempt to read the entire buffer.
+ else
+ SizeToRead=(size_t)Min((int64)BufSize,Size);
+ int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
+ if (ReadSize==0)
+ break;
+
+ if ((++BlockCount & 0xf)==0)
+ {
+#ifndef SILENT
+#ifndef GUI
+ if ((Flags & CALCFSUM_SHOWALL)!=0)
+#endif
+ uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(BlockCount*int64(BufSize),FileLength));
+#endif
+ Wait();
+ }
+
+ if (CRC32!=NULL)
+ HashCRC.Update(&Data[0],ReadSize);
+ if (Blake2!=NULL)
+ HashBlake2.Update(&Data[0],ReadSize);
+
+ if (Size!=INT64NDF)
+ Size-=ReadSize;
+ }
+#ifndef GUI
+ if ((Flags & CALCFSUM_SHOWALL)!=0)
+#endif
+ uiMsg(UIEVENT_FILESUMEND);
+
+ if (CRC32!=NULL)
+ *CRC32=HashCRC.GetCRC32();
+ if (Blake2!=NULL)
+ {
+ HashValue Result;
+ HashBlake2.Result(&Result);
+ memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
+ }
+}
+#endif
+
+
+bool RenameFile(const wchar *SrcName,const wchar *DestName)
+{
+#ifdef _WIN_ALL
+ bool Success=MoveFile(SrcName,DestName)!=0;
+ if (!Success)
+ {
+ wchar LongName1[NM],LongName2[NM];
+ if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
+ GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
+ Success=MoveFile(LongName1,LongName2)!=0;
+ }
+ return Success;
+#else
+ char SrcNameA[NM],DestNameA[NM];
+ WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
+ WideToChar(DestName,DestNameA,ASIZE(DestNameA));
+ bool Success=rename(SrcNameA,DestNameA)==0;
+#ifdef _ANDROID
+ if (!Success)
+ Success=JniRename(SrcName,DestName); // If external card is read-only for usual file API.
+ if (Success)
+ {
+ JniFileNotify(SrcName,true);
+ JniFileNotify(DestName,false);
+ }
+#endif
+ return Success;
+#endif
+}
+
+
+bool DelFile(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ bool Success=DeleteFile(Name)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=DeleteFile(LongName)!=0;
+ }
+ return Success;
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ bool Success=remove(NameA)==0;
+#ifdef _ANDROID
+ if (!Success)
+ Success=JniDelete(Name);
+ if (Success)
+ JniFileNotify(Name,true);
+#endif
+ return Success;
+#endif
+}
+
+
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State)
+{
+ HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
+ if (hFile==INVALID_HANDLE_VALUE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
+ }
+ if (hFile==INVALID_HANDLE_VALUE)
+ return false;
+ SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
+ DWORD Result;
+ int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
+ sizeof(NewState),NULL,0,&Result,NULL);
+ CloseHandle(hFile);
+ return RetCode!=0;
+}
+#endif
+
+
+
+
+
+
+
+
+
+
diff --git a/filefn.hpp b/filefn.hpp
new file mode 100644
index 0000000..b06cc64
--- /dev/null
+++ b/filefn.hpp
@@ -0,0 +1,47 @@
+#ifndef _RAR_FILEFN_
+#define _RAR_FILEFN_
+
+enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
+
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
+bool CreatePath(const wchar *Path,bool SkipLastName);
+void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
+bool IsRemovable(const wchar *Name);
+
+#ifndef SFX_MODULE
+int64 GetFreeDisk(const wchar *Name);
+#endif
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+bool IsFAT(const wchar *Root);
+#endif
+
+bool FileExist(const wchar *Name);
+bool WildFileExist(const wchar *Name);
+bool IsDir(uint Attr);
+bool IsUnreadable(uint Attr);
+bool IsLink(uint Attr);
+void SetSFXMode(const wchar *FileName);
+void EraseDiskContents(const wchar *FileName);
+bool IsDeleteAllowed(uint FileAttr);
+void PrepareToDelete(const wchar *Name);
+uint GetFileAttr(const wchar *Name);
+bool SetFileAttr(const wchar *Name,uint Attr);
+
+enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWALL=2,CALCFSUM_CURPOS=4};
+
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
+
+bool RenameFile(const wchar *SrcName,const wchar *DestName);
+bool DelFile(const wchar *Name);
+bool DelDir(const wchar *Name);
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State);
+#endif
+
+
+
+
+
+#endif
diff --git a/filestr.cpp b/filestr.cpp
new file mode 100644
index 0000000..5fa946a
--- /dev/null
+++ b/filestr.cpp
@@ -0,0 +1,196 @@
+#include "rar.hpp"
+
+static bool IsUnicode(byte *Data,int Size);
+
+bool ReadTextFile(
+ const wchar *Name,
+ StringList *List,
+ bool Config,
+ bool AbortOnError,
+ RAR_CHARSET SrcCharset,
+ bool Unquote,
+ bool SkipComments,
+ bool ExpandEnvStr)
+{
+ wchar FileName[NM];
+ *FileName=0;
+
+ if (Name!=NULL)
+ if (Config)
+ GetConfigName(Name,FileName,ASIZE(FileName),true,false);
+ else
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+
+ File SrcFile;
+ if (*FileName!=0)
+ {
+ bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
+
+ if (!OpenCode)
+ {
+ if (AbortOnError)
+ ErrHandler.Exit(RARX_OPEN);
+ return false;
+ }
+ }
+ else
+ SrcFile.SetHandleType(FILE_HANDLESTD);
+
+ unsigned int DataSize=0,ReadSize;
+ const int ReadBlock=4096;
+
+ // Our algorithm below needs at least two trailing zeroes after data.
+ // So for Unicode we provide 2 Unicode zeroes and one more byte
+ // in case read Unicode data contains uneven number of bytes.
+ const size_t ZeroPadding=5;
+
+ Array Data(ReadBlock+ZeroPadding);
+ while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
+ {
+ DataSize+=ReadSize;
+ Data.Add(ReadSize); // Always have ReadBlock available for next data.
+ }
+
+ memset(&Data[DataSize],0,ZeroPadding); // Provide at least 2 Unicode zero bytes.
+
+ Array WideStr;
+
+ if (SrcCharset==RCH_UNICODE ||
+ SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize))
+ {
+ // Unicode in native system format, can be more than 2 bytes per character.
+ Array DataW(Data.Size()/2+1);
+ for (size_t I=2;I=CurStr;SpacePtr--)
+ {
+ if (*SpacePtr!=' ' && *SpacePtr!='\t')
+ break;
+ *SpacePtr=0;
+ }
+ if (*CurStr!=0)
+ {
+ size_t Length=wcslen(CurStr);
+
+ if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"')
+ {
+ CurStr[Length-1]=0;
+ CurStr++;
+ }
+
+ bool Expanded=false;
+#ifdef _WIN_ALL
+ if (ExpandEnvStr && *CurStr=='%')
+ {
+ // Expanding environment variables in Windows version.
+
+ wchar ExpName[NM];
+ *ExpName=0;
+ DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
+ Expanded=Result!=0 && ResultAddString(ExpName);
+ }
+#endif
+ if (!Expanded)
+ List->AddString(CurStr);
+ }
+ CurStr=NextStr+1;
+ while (*CurStr=='\r' || *CurStr=='\n')
+ CurStr++;
+ }
+ }
+ else
+ {
+ char *CurStr=&Data[0];
+ while (*CurStr!=0)
+ {
+ char *NextStr=CurStr,*CmtPtr=NULL;
+ while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
+ {
+ if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
+ {
+ *NextStr=0;
+ CmtPtr=NextStr;
+ }
+ NextStr++;
+ }
+ *NextStr=0;
+ for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
+ {
+ if (*SpacePtr!=' ' && *SpacePtr!='\t')
+ break;
+ *SpacePtr=0;
+ }
+ if (*CurStr!=0)
+ {
+ if (Unquote && *CurStr=='\"')
+ {
+ size_t Length=strlen(CurStr);
+ if (CurStr[Length-1]=='\"')
+ {
+ CurStr[Length-1]=0;
+ CurStr++;
+ }
+ }
+#if defined(_WIN_ALL)
+ if (SrcCharset==RCH_OEM)
+ OemToCharA(CurStr,CurStr);
+#endif
+
+ bool Expanded=false;
+
+ WideStr.Alloc(strlen(CurStr)+1);
+ CharToWide(CurStr,&WideStr[0],WideStr.Size());
+#ifdef _WIN_ALL
+ if (ExpandEnvStr && *CurStr=='%')
+ {
+ // Expanding environment variables in Windows version.
+ wchar ExpName[NM];
+ DWORD Result=ExpandEnvironmentStringsW(&WideStr[0],ExpName,ASIZE(ExpName));
+ Expanded=Result!=0 && ResultAddString(ExpName);
+ }
+#endif
+ if (!Expanded)
+ List->AddString(&WideStr[0]);
+ }
+ CurStr=NextStr+1;
+ while (*CurStr=='\r' || *CurStr=='\n')
+ CurStr++;
+ }
+ }
+ return true;
+}
+
+
+bool IsUnicode(byte *Data,int Size)
+{
+ if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe)
+ return false;
+ for (int I=2;IError=false;
+ if (*FindMask==0)
+ return false;
+#ifdef _WIN_ALL
+ if (FirstCall)
+ {
+ if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
+ return false;
+ }
+ else
+ if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
+ return false;
+#else
+ if (FirstCall)
+ {
+ wchar DirName[NM];
+ wcsncpyz(DirName,FindMask,ASIZE(DirName));
+ RemoveNameFromPath(DirName);
+ if (*DirName==0)
+ wcscpy(DirName,L".");
+ char DirNameA[NM];
+ WideToChar(DirName,DirNameA,ASIZE(DirNameA));
+ if ((dirp=opendir(DirNameA))==NULL)
+ {
+ fd->Error=(errno!=ENOENT);
+ return false;
+ }
+ }
+ while (1)
+ {
+ struct dirent *ent=readdir(dirp);
+ if (ent==NULL)
+ return false;
+ if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
+ continue;
+ wchar Name[NM];
+ if (!CharToWide(ent->d_name,Name,ASIZE(Name)))
+ uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
+
+ if (CmpName(FindMask,Name,MATCH_NAMES))
+ {
+ wchar FullName[NM];
+ wcscpy(FullName,FindMask);
+ *PointToName(FullName)=0;
+ if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
+ {
+ uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
+ return false;
+ }
+ wcscat(FullName,Name);
+ if (!FastFind(FullName,fd,GetSymLink))
+ {
+ ErrHandler.OpenErrorMsg(FullName);
+ continue;
+ }
+ wcscpy(fd->Name,FullName);
+ break;
+ }
+ }
+#endif
+ fd->Flags=0;
+ fd->IsDir=IsDir(fd->FileAttr);
+ fd->IsLink=IsLink(fd->FileAttr);
+
+ FirstCall=false;
+ wchar *NameOnly=PointToName(fd->Name);
+ if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
+ return Next(fd);
+ return true;
+}
+
+
+bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
+{
+ fd->Error=false;
+#ifndef _UNIX
+ if (IsWildcard(FindMask))
+ return false;
+#endif
+#ifdef _WIN_ALL
+ HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
+ if (hFind==INVALID_HANDLE_VALUE)
+ return false;
+ FindClose(hFind);
+#else
+ char FindMaskA[NM];
+ WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
+
+ struct stat st;
+ if (GetSymLink)
+ {
+#ifdef SAVE_LINKS
+ if (lstat(FindMaskA,&st)!=0)
+#else
+ if (stat(FindMaskA,&st)!=0)
+#endif
+ {
+ fd->Error=(errno!=ENOENT);
+ return false;
+ }
+ }
+ else
+ if (stat(FindMaskA,&st)!=0)
+ {
+ fd->Error=(errno!=ENOENT);
+ return false;
+ }
+ fd->FileAttr=st.st_mode;
+ fd->Size=st.st_size;
+ fd->mtime=st.st_mtime;
+ fd->atime=st.st_atime;
+ fd->ctime=st.st_ctime;
+ wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
+#endif
+ fd->Flags=0;
+ fd->IsDir=IsDir(fd->FileAttr);
+ fd->IsLink=IsLink(fd->FileAttr);
+
+ return true;
+}
+
+
+#ifdef _WIN_ALL
+HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
+{
+ WIN32_FIND_DATA FindData;
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ hFind=FindFirstFile(Mask,&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ wchar LongMask[NM];
+ if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
+ hFind=FindFirstFile(LongMask,&FindData);
+ }
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ int SysErr=GetLastError();
+ fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND &&
+ SysErr!=ERROR_PATH_NOT_FOUND &&
+ SysErr!=ERROR_NO_MORE_FILES);
+ }
+ }
+ else
+ if (!FindNextFile(hFind,&FindData))
+ {
+ hFind=INVALID_HANDLE_VALUE;
+ fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
+ }
+
+ if (hFind!=INVALID_HANDLE_VALUE)
+ {
+ wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
+ SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
+ fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
+ fd->FileAttr=FindData.dwFileAttributes;
+ fd->ftCreationTime=FindData.ftCreationTime;
+ fd->ftLastAccessTime=FindData.ftLastAccessTime;
+ fd->ftLastWriteTime=FindData.ftLastWriteTime;
+ fd->mtime=FindData.ftLastWriteTime;
+ fd->ctime=FindData.ftCreationTime;
+ fd->atime=FindData.ftLastAccessTime;
+
+
+ }
+ fd->Flags=0;
+ return hFind;
+}
+#endif
+
diff --git a/find.hpp b/find.hpp
new file mode 100644
index 0000000..250637f
--- /dev/null
+++ b/find.hpp
@@ -0,0 +1,49 @@
+#ifndef _RAR_FINDDATA_
+#define _RAR_FINDDATA_
+
+enum FINDDATA_FLAGS {
+ FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
+};
+
+struct FindData
+{
+ wchar Name[NM];
+ uint64 Size;
+ uint FileAttr;
+ bool IsDir;
+ bool IsLink;
+ RarTime mtime;
+ RarTime ctime;
+ RarTime atime;
+#ifdef _WIN_ALL
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime;
+#endif
+ uint Flags;
+ bool Error;
+};
+
+class FindFile
+{
+ private:
+#ifdef _WIN_ALL
+ static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
+#endif
+
+ wchar FindMask[NM];
+ bool FirstCall;
+#ifdef _WIN_ALL
+ HANDLE hFind;
+#else
+ DIR *dirp;
+#endif
+ public:
+ FindFile();
+ ~FindFile();
+ void SetMask(const wchar *Mask);
+ bool Next(FindData *fd,bool GetSymLink=false);
+ static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
+};
+
+#endif
diff --git a/getbits.cpp b/getbits.cpp
new file mode 100644
index 0000000..e57b3e3
--- /dev/null
+++ b/getbits.cpp
@@ -0,0 +1,52 @@
+#include "rar.hpp"
+
+BitInput::BitInput(bool AllocBuffer)
+{
+ ExternalBuffer=false;
+ if (AllocBuffer)
+ {
+ // getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
+ // So let's allocate 3 additional bytes for situation, when we need to
+ // read only 1 byte from the last position of buffer and avoid a crash
+ // from access to next 3 bytes, which contents we do not need.
+ size_t BufSize=MAX_SIZE+3;
+ InBuf=new byte[BufSize];
+
+ // Ensure that we get predictable results when accessing bytes in area
+ // not filled with read data.
+ memset(InBuf,0,BufSize);
+ }
+ else
+ InBuf=NULL;
+}
+
+
+BitInput::~BitInput()
+{
+ if (!ExternalBuffer)
+ delete[] InBuf;
+}
+
+
+void BitInput::faddbits(uint Bits)
+{
+ // Function wrapped version of inline addbits to save code size.
+ addbits(Bits);
+}
+
+
+uint BitInput::fgetbits()
+{
+ // Function wrapped version of inline getbits to save code size.
+ return(getbits());
+}
+
+
+void BitInput::SetExternalBuffer(byte *Buf)
+{
+ if (InBuf!=NULL && !ExternalBuffer)
+ delete[] InBuf;
+ InBuf=Buf;
+ ExternalBuffer=true;
+}
+
diff --git a/getbits.hpp b/getbits.hpp
new file mode 100644
index 0000000..6a5430b
--- /dev/null
+++ b/getbits.hpp
@@ -0,0 +1,68 @@
+#ifndef _RAR_GETBITS_
+#define _RAR_GETBITS_
+
+class BitInput
+{
+ public:
+ enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
+
+ int InAddr; // Curent byte position in the buffer.
+ int InBit; // Current bit position in the current byte.
+
+ bool ExternalBuffer;
+ public:
+ BitInput(bool AllocBuffer);
+ ~BitInput();
+
+ byte *InBuf; // Dynamically allocated input buffer.
+
+ void InitBitInput()
+ {
+ InAddr=InBit=0;
+ }
+
+ // Move forward by 'Bits' bits.
+ void addbits(uint Bits)
+ {
+ Bits+=InBit;
+ InAddr+=Bits>>3;
+ InBit=Bits&7;
+ }
+
+ // Return 16 bits from current position in the buffer.
+ // Bit at (InAddr,InBit) has the highest position in returning data.
+ uint getbits()
+ {
+ uint BitField=(uint)InBuf[InAddr] << 16;
+ BitField|=(uint)InBuf[InAddr+1] << 8;
+ BitField|=(uint)InBuf[InAddr+2];
+ BitField >>= (8-InBit);
+ return(BitField & 0xffff);
+ }
+
+ // Return 32 bits from current position in the buffer.
+ // Bit at (InAddr,InBit) has the highest position in returning data.
+ uint getbits32()
+ {
+ uint BitField=(uint)InBuf[InAddr] << 24;
+ BitField|=(uint)InBuf[InAddr+1] << 16;
+ BitField|=(uint)InBuf[InAddr+2] << 8;
+ BitField|=(uint)InBuf[InAddr+3];
+ BitField <<= InBit;
+ BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
+ return(BitField & 0xffffffff);
+ }
+
+ void faddbits(uint Bits);
+ uint fgetbits();
+
+ // Check if buffer has enough space for IncPtr bytes. Returns 'true'
+ // if buffer will be overflown.
+ bool Overflow(uint IncPtr)
+ {
+ return(InAddr+IncPtr>=MAX_SIZE);
+ }
+
+ void SetExternalBuffer(byte *Buf);
+};
+#endif
diff --git a/global.cpp b/global.cpp
new file mode 100644
index 0000000..3975813
--- /dev/null
+++ b/global.cpp
@@ -0,0 +1,7 @@
+#define INCLUDEGLOBAL
+
+#if defined(__BORLANDC__) || defined(_MSC_VER)
+#pragma hdrstop
+#endif
+
+#include "rar.hpp"
diff --git a/global.hpp b/global.hpp
new file mode 100644
index 0000000..35c6cf9
--- /dev/null
+++ b/global.hpp
@@ -0,0 +1,14 @@
+#ifndef _RAR_GLOBAL_
+#define _RAR_GLOBAL_
+
+#ifdef INCLUDEGLOBAL
+ #define EXTVAR
+#else
+ #define EXTVAR extern
+#endif
+
+EXTVAR ErrorHandler ErrHandler;
+
+
+
+#endif
diff --git a/hardlinks.cpp b/hardlinks.cpp
new file mode 100644
index 0000000..cf0b25a
--- /dev/null
+++ b/hardlinks.cpp
@@ -0,0 +1,34 @@
+bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+{
+ SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
+
+ if (!FileExist(NameExisting))
+ return false;
+ CreatePath(NameNew,true);
+
+#ifdef _WIN_ALL
+ bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#elif defined(_UNIX)
+ char NameExistingA[NM],NameNewA[NM];
+ WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA));
+ WideToChar(NameNew,NameNewA,ASIZE(NameNewA));
+ bool Success=link(NameExistingA,NameNewA)==0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#else
+ return false;
+#endif
+}
+
diff --git a/hash.cpp b/hash.cpp
new file mode 100644
index 0000000..578e32f
--- /dev/null
+++ b/hash.cpp
@@ -0,0 +1,128 @@
+#include "rar.hpp"
+
+void HashValue::Init(HASH_TYPE Type)
+{
+ HashValue::Type=Type;
+
+ // Zero length data CRC32 is 0. It is important to set it when creating
+ // headers with no following data like directories or symlinks.
+ if (Type==HASH_RAR14 || Type==HASH_CRC32)
+ CRC32=0;
+ if (Type==HASH_BLAKE2)
+ {
+ // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
+ // is BLAKE2sp hash of empty data. We init the structure to this value,
+ // so if we create a file or service header with no following data like
+ // "file copy" or "symlink", we set the checksum to proper value avoiding
+ // additional header type or size checks when extracting.
+ static byte EmptyHash[32]={
+ 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
+ 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
+ 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
+ 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
+ };
+ memcpy(Digest,EmptyHash,sizeof(Digest));
+ }
+}
+
+
+bool HashValue::operator == (const HashValue &cmp)
+{
+ if (Type==HASH_NONE || cmp.Type==HASH_NONE)
+ return true;
+ if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
+ Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
+ return CRC32==cmp.CRC32;
+ if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
+ return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
+ return false;
+}
+
+
+DataHash::DataHash()
+{
+ HashType=HASH_NONE;
+#ifdef RAR_SMP
+ ThPool=NULL;
+ MaxThreads=0;
+#endif
+}
+
+
+DataHash::~DataHash()
+{
+#ifdef RAR_SMP
+ DestroyThreadPool(ThPool);
+#endif
+ cleandata(&blake2ctx, sizeof(blake2ctx));
+ cleandata(&CurCRC32, sizeof(CurCRC32));
+}
+
+
+void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
+{
+ HashType=Type;
+ if (Type==HASH_RAR14)
+ CurCRC32=0;
+ if (Type==HASH_CRC32)
+ CurCRC32=0xffffffff; // Initial CRC32 value.
+ if (Type==HASH_BLAKE2)
+ blake2sp_init( &blake2ctx );
+#ifdef RAR_SMP
+ DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads);
+#endif
+}
+
+
+void DataHash::Update(const void *Data,size_t DataSize)
+{
+#ifndef SFX_MODULE
+ if (HashType==HASH_RAR14)
+ CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
+#endif
+ if (HashType==HASH_CRC32)
+ CurCRC32=CRC32(CurCRC32,Data,DataSize);
+
+ if (HashType==HASH_BLAKE2)
+ {
+#ifdef RAR_SMP
+ if (MaxThreads>1 && ThPool==NULL)
+ ThPool=CreateThreadPool();
+ blake2ctx.ThPool=ThPool;
+ blake2ctx.MaxThreads=MaxThreads;
+#endif
+ blake2sp_update( &blake2ctx, (byte *)Data, DataSize);
+ }
+}
+
+
+void DataHash::Result(HashValue *Result)
+{
+ Result->Type=HashType;
+ if (HashType==HASH_RAR14)
+ Result->CRC32=CurCRC32;
+ if (HashType==HASH_CRC32)
+ Result->CRC32=CurCRC32^0xffffffff;
+ if (HashType==HASH_BLAKE2)
+ {
+ // Preserve the original context, so we can continue hashing if necessary.
+ blake2sp_state res=blake2ctx;
+ blake2sp_final( &res, Result->Digest );
+ }
+}
+
+
+uint DataHash::GetCRC32()
+{
+ return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
+}
+
+
+bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
+{
+ HashValue Final;
+ Result(&Final);
+ if (Key!=NULL)
+ ConvertHashToMAC(&Final,Key);
+ return Final==*CmpValue;
+}
diff --git a/hash.hpp b/hash.hpp
new file mode 100644
index 0000000..dae31d1
--- /dev/null
+++ b/hash.hpp
@@ -0,0 +1,52 @@
+#ifndef _RAR_DATAHASH_
+#define _RAR_DATAHASH_
+
+enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
+
+struct HashValue
+{
+ void Init(HASH_TYPE Type);
+ bool operator == (const HashValue &cmp);
+ bool operator != (const HashValue &cmp) {return !(*this==cmp);}
+
+ HASH_TYPE Type;
+ union
+ {
+ uint CRC32;
+ byte Digest[SHA256_DIGEST_SIZE];
+ };
+};
+
+
+#ifdef RAR_SMP
+class ThreadPool;
+class DataHash;
+#endif
+
+
+class DataHash
+{
+ private:
+ HASH_TYPE HashType;
+ uint CurCRC32;
+ blake2sp_state blake2ctx;
+
+#ifdef RAR_SMP
+ ThreadPool *ThPool;
+
+ uint MaxThreads;
+ // Upper limit for maximum threads to prevent wasting threads in pool.
+ static const uint MaxHashThreads=8;
+#endif
+ public:
+ DataHash();
+ ~DataHash();
+ void Init(HASH_TYPE Type,uint MaxThreads);
+ void Update(const void *Data,size_t DataSize);
+ void Result(HashValue *Result);
+ uint GetCRC32();
+ bool Cmp(HashValue *CmpValue,byte *Key);
+ HASH_TYPE Type() {return HashType;}
+};
+
+#endif
diff --git a/headers.cpp b/headers.cpp
new file mode 100644
index 0000000..5c8d486
--- /dev/null
+++ b/headers.cpp
@@ -0,0 +1,62 @@
+#include "rar.hpp"
+
+void FileHeader::Reset(size_t SubDataSize)
+{
+ SubData.Alloc(SubDataSize);
+ BaseBlock::Reset();
+#ifndef SHELL_EXT
+ FileHash.Init(HASH_NONE);
+#endif
+ mtime.Reset();
+ atime.Reset();
+ ctime.Reset();
+ SplitBefore=false;
+ SplitAfter=false;
+
+ UnknownUnpSize=0;
+
+ SubFlags=0; // Important for RAR 3.0 subhead.
+
+ CryptMethod=CRYPT_NONE;
+ Encrypted=false;
+ SaltSet=false;
+ UsePswCheck=false;
+ UseHashKey=false;
+ Lg2Count=0;
+
+ Solid=false;
+ Dir=false;
+ WinSize=0;
+ Inherited=false;
+ SubBlock=false;
+ CommentInHeader=false;
+ Version=false;
+ LargeFile=false;
+
+ RedirType=FSREDIR_NONE;
+ UnixOwnerSet=false;
+}
+
+
+FileHeader& FileHeader::operator = (FileHeader &hd)
+{
+ SubData.Reset();
+ memcpy(this,&hd,sizeof(*this));
+ SubData.CleanData();
+ SubData=hd.SubData;
+ return *this;
+}
+
+
+void MainHeader::Reset()
+{
+ HighPosAV=0;
+ PosAV=0;
+ CommentInHeader=false;
+ PackComment=false;
+ Locator=false;
+ QOpenOffset=0;
+ QOpenMaxSize=0;
+ RROffset=0;
+ RRMaxSize=0;
+}
diff --git a/headers.hpp b/headers.hpp
new file mode 100644
index 0000000..9486478
--- /dev/null
+++ b/headers.hpp
@@ -0,0 +1,380 @@
+#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
+#define SIZEOF_LONGBLOCKHEAD 11
+#define SIZEOF_SUBBLOCKHEAD 14
+#define SIZEOF_COMMHEAD 13
+#define SIZEOF_PROTECTHEAD 26
+#define SIZEOF_AVHEAD 14
+#define SIZEOF_SIGNHEAD 15
+#define SIZEOF_UOHEAD 18
+#define SIZEOF_MACHEAD 22
+#define SIZEOF_EAHEAD 24
+#define SIZEOF_BEEAHEAD 24
+#define SIZEOF_STREAMHEAD 26
+
+#define VER_PACK 29
+#define VER_PACK5 50 // It is stored as 0, but we subtract 50 when saving an archive.
+#define VER_UNPACK 29
+#define VER_UNPACK5 50 // It is stored as 0, but we add 50 when reading an archive.
+
+#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
+};
+
+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;
+ }
+};
+
+
+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.
+ void Reset();
+};
+
+
+struct FileHeader:BlockHeader
+{
+ byte HostOS;
+ byte UnpVer;
+ byte Method;
+ union {
+ uint FileAttr;
+ uint SubFlags;
+ };
+ wchar FileName[NM];
+
+ Array SubData;
+
+ RarTime mtime;
+ RarTime ctime;
+ RarTime atime;
+
+ int64 PackSize;
+ int64 UnpSize;
+ int64 MaxSize; // Reserve 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.
+ size_t 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 child subheader blocks in archives.
+ bool SubBlock;
+
+ HOST_SYSTEM_TYPE HSType;
+
+ FILE_SYSTEM_REDIRECT RedirType;
+ wchar RedirName[NM];
+ 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(wcscmp(FileName,Name)==0);
+ }
+
+ 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 AVHeader:BaseBlock
+{
+ byte UnpVer;
+ byte Method;
+ byte AVVer;
+ uint AVInfoCRC;
+};
+
+
+struct SignHeader:BaseBlock
+{
+ uint CreationTime;
+ ushort ArcNameSize;
+ ushort UserNameSize;
+};
+
+
+struct UnixOwnersHeader:SubBlockHeader
+{
+ ushort OwnerNameSize;
+ ushort GroupNameSize;
+/* dummy */
+ char OwnerName[256];
+ char GroupName[256];
+};
+
+
+struct EAHeader:SubBlockHeader
+{
+ uint UnpSize;
+ byte UnpVer;
+ byte Method;
+ uint EACRC;
+};
+
+
+struct StreamHeader:SubBlockHeader
+{
+ uint UnpSize;
+ byte UnpVer;
+ byte Method;
+ uint StreamCRC;
+ ushort StreamNameSize;
+ char StreamName[260];
+};
+
+
+struct MacFInfoHeader:SubBlockHeader
+{
+ uint fileType;
+ uint fileCreator;
+};
+
+
+#endif
diff --git a/headers5.hpp b/headers5.hpp
new file mode 100644
index 0000000..4f403c2
--- /dev/null
+++ b/headers5.hpp
@@ -0,0 +1,99 @@
+#ifndef _RAR_HEADERS5_
+#define _RAR_HEADERS5_
+
+#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
+#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
+
+// RAR 5.0 block flags common for all blocks.
+
+// Additional extra area is present in the end of block header.
+#define HFL_EXTRA 0x0001
+// Additional data area is present in the end of block header.
+#define HFL_DATA 0x0002
+// Unknown blocks with this flag must be skipped when updating an archive.
+#define HFL_SKIPIFUNKNOWN 0x0004
+// Data area of this block is continuing from previous volume.
+#define HFL_SPLITBEFORE 0x0008
+// Data area of this block is continuing in next volume.
+#define HFL_SPLITAFTER 0x0010
+// Block depends on preceding file block.
+#define HFL_CHILD 0x0020
+// Preserve a child block if host is modified.
+#define HFL_INHERITED 0x0040
+
+// RAR 5.0 main archive header specific flags.
+#define MHFL_VOLUME 0x0001 // Volume.
+#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
+#define MHFL_SOLID 0x0004 // Solid archive.
+#define MHFL_PROTECT 0x0008 // Recovery record is present.
+#define MHFL_LOCK 0x0010 // Locked archive.
+
+// RAR 5.0 file header specific flags.
+#define FHFL_DIRECTORY 0x0001 // Directory.
+#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
+#define FHFL_CRC32 0x0004 // CRC32 field is present.
+#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
+
+// RAR 5.0 end of archive header specific flags.
+#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
+
+// RAR 5.0 archive encryption header specific flags.
+#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
+
+
+// RAR 5.0 file compression flags.
+#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm.
+#define FCI_ALGO_BIT1 0x0002 // 0 .. 63.
+#define FCI_ALGO_BIT2 0x0004
+#define FCI_ALGO_BIT3 0x0008
+#define FCI_ALGO_BIT4 0x0010
+#define FCI_ALGO_BIT5 0x0020
+#define FCI_SOLID 0x0040 // Solid flag.
+#define FCI_METHOD_BIT0 0x0080 // Compression method.
+#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used).
+#define FCI_METHOD_BIT2 0x0200
+#define FCI_DICT_BIT0 0x0400 // Dictionary size.
+#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB.
+#define FCI_DICT_BIT2 0x1000
+#define FCI_DICT_BIT3 0x2000
+
+// Main header extra field values.
+#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
+
+// Flags for MHEXTRA_LOCATOR.
+#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
+#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
+
+// File and service header extra field values.
+#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
+#define FHEXTRA_HASH 0x02 // File hash.
+#define FHEXTRA_HTIME 0x03 // High precision file time.
+#define FHEXTRA_VERSION 0x04 // File version information.
+#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
+#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
+#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
+
+
+// Hash type values for FHEXTRA_HASH.
+#define FHEXTRA_HASH_BLAKE2 0x00
+
+// Flags for FHEXTRA_HTIME.
+#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
+#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
+#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
+#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
+
+// Flags for FHEXTRA_CRYPT.
+#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
+#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
+
+// Flags for FHEXTRA_REDIR.
+#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
+
+// Flags for FHEXTRA_UOWNER.
+#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
+#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
+#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
+#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
+
+#endif
diff --git a/isnt.cpp b/isnt.cpp
new file mode 100644
index 0000000..6fadec0
--- /dev/null
+++ b/isnt.cpp
@@ -0,0 +1,24 @@
+#include "rar.hpp"
+
+#ifdef _WIN_ALL
+DWORD WinNT()
+{
+ static int dwPlatformId=-1;
+ static DWORD dwMajorVersion,dwMinorVersion;
+ if (dwPlatformId==-1)
+ {
+ OSVERSIONINFO WinVer;
+ WinVer.dwOSVersionInfoSize=sizeof(WinVer);
+ GetVersionEx(&WinVer);
+ dwPlatformId=WinVer.dwPlatformId;
+ dwMajorVersion=WinVer.dwMajorVersion;
+ dwMinorVersion=WinVer.dwMinorVersion;
+ }
+ DWORD Result=0;
+ if (dwPlatformId==VER_PLATFORM_WIN32_NT)
+ Result=dwMajorVersion*0x100+dwMinorVersion;
+
+
+ return Result;
+}
+#endif
diff --git a/isnt.hpp b/isnt.hpp
new file mode 100644
index 0000000..877d880
--- /dev/null
+++ b/isnt.hpp
@@ -0,0 +1,12 @@
+#ifndef _RAR_ISNT_
+#define _RAR_ISNT_
+
+enum WINNT_VERSION {
+ WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
+ WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
+ WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
+};
+
+DWORD WinNT();
+
+#endif
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..0811276
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,42 @@
+ ****** ***** ****** UnRAR - free utility for RAR archives
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ****** ******* ****** License for use and distribution of
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ** ** ** ** ** ** FREE portable version
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ The source code of UnRAR utility is freeware. This means:
+
+ 1. All copyrights to RAR and the utility UnRAR are exclusively
+ owned by the author - Alexander Roshal.
+
+ 2. UnRAR source code may be used in any software to handle
+ RAR archives without limitations free of charge, but cannot be
+ used to develop RAR (WinRAR) compatible archiver and to
+ re-create RAR compression algorithm, which is proprietary.
+ Distribution of modified UnRAR source code in separate form
+ or as a part of other software is permitted, provided that
+ full text of this paragraph, starting from "UnRAR source code"
+ words, is included in license, or in documentation if license
+ is not available, and in source code comments of resulting package.
+
+ 3. The UnRAR utility may be freely distributed. It is allowed
+ to distribute UnRAR inside of other software packages.
+
+ 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
+ NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
+ YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
+ DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
+ OR MISUSING THIS SOFTWARE.
+
+ 5. Installing and using the UnRAR utility signifies acceptance of
+ these terms and conditions of the license.
+
+ 6. If you don't agree with terms of the license you must remove
+ UnRAR files from your storage devices and cease to use the
+ utility.
+
+ Thank you for your interest in RAR and UnRAR.
+
+
+ Alexander L. Roshal
diff --git a/list.cpp b/list.cpp
new file mode 100644
index 0000000..9e69ef7
--- /dev/null
+++ b/list.cpp
@@ -0,0 +1,474 @@
+#include "rar.hpp"
+
+static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare);
+static void ListSymLink(Archive &Arc);
+static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize);
+static void ListOldSubHeader(Archive &Arc);
+static void ListNewSubHeader(CommandData *Cmd,Archive &Arc);
+
+void ListArchive(CommandData *Cmd)
+{
+ int64 SumPackSize=0,SumUnpSize=0;
+ uint ArcCount=0,SumFileCount=0;
+ bool Technical=(Cmd->Command[1]=='T');
+ bool ShowService=Technical && Cmd->Command[2]=='A';
+ bool Bare=(Cmd->Command[1]=='B');
+ bool Verbose=(Cmd->Command[0]=='V');
+
+ wchar ArcName[NM];
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ {
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean(); // Clean user entered password before processing next archive.
+
+ Archive Arc(Cmd);
+#ifdef _WIN_ALL
+ Arc.RemoveSequentialFlag();
+#endif
+ if (!Arc.WOpen(ArcName))
+ continue;
+ bool FileMatched=true;
+ while (1)
+ {
+ int64 TotalPackSize=0,TotalUnpSize=0;
+ uint FileCount=0;
+ if (Arc.IsArchive(true))
+ {
+ bool TitleShown=false;
+ if (!Bare)
+ {
+ Arc.ViewComment();
+ mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName);
+ mprintf(L"\n%s: ",St(MListDetails));
+ uint SetCount=0;
+ const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5");
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt);
+ if (Arc.Solid)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid));
+ if (Arc.SFXSize>0)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX));
+ if (Arc.Volume)
+ if (Arc.Format==RARFMT50)
+ {
+ // RAR 5.0 archives store the volume number in main header,
+ // so it is already available now.
+ if (SetCount++ > 0)
+ mprintf(L", ");
+ mprintf(St(MVolumeNumber),Arc.VolNumber+1);
+ }
+ else
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume));
+ if (Arc.Protected)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR));
+ if (Arc.Locked)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock));
+ if (Arc.Encrypted)
+ mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead));
+ mprintf(L"\n");
+ }
+
+ wchar VolNumText[50];
+ *VolNumText=0;
+ while(Arc.ReadHeader()>0)
+ {
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType==HEAD_ENDARC)
+ {
+#ifndef SFX_MODULE
+ // Only RAR 1.5 archives store the volume number in end record.
+ if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15)
+ swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %d",St(MListVolume),Arc.VolNumber+1);
+#endif
+ if (Technical && ShowService)
+ {
+ mprintf(L"\n%12ls: %ls",St(MListService),L"EOF");
+ if (*VolNumText!=0)
+ mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText);
+ mprintf(L"\n");
+ }
+ break;
+ }
+ switch(HeaderType)
+ {
+ case HEAD_FILE:
+ FileMatched=Cmd->IsProcessFile(Arc.FileHead)!=0;
+ if (FileMatched)
+ {
+ ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare);
+ if (!Arc.FileHead.SplitBefore)
+ {
+ TotalUnpSize+=Arc.FileHead.UnpSize;
+ FileCount++;
+ }
+ TotalPackSize+=Arc.FileHead.PackSize;
+ }
+ break;
+ case HEAD_SERVICE:
+ if (FileMatched && !Bare)
+ {
+ if (Technical && ShowService)
+ ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false);
+ }
+ break;
+ }
+ Arc.SeekToNext();
+ }
+ if (!Bare && !Technical)
+ if (TitleShown)
+ {
+ wchar UnpSizeText[20];
+ itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText));
+
+ wchar PackSizeText[20];
+ itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText));
+
+ if (Verbose)
+ {
+ mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
+ mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText,
+ PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize),
+ VolNumText,FileCount);
+ }
+ else
+ {
+ mprintf(L"\n----------- --------- ---------- ----- ----");
+ mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount);
+ }
+
+ SumFileCount+=FileCount;
+ SumUnpSize+=TotalUnpSize;
+ SumPackSize+=TotalPackSize;
+ mprintf(L"\n");
+ }
+ else
+ mprintf(St(MListNoFiles));
+
+ ArcCount++;
+
+#ifndef NOVOLUME
+ if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter ||
+ Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) &&
+ MergeArchive(Arc,NULL,false,Cmd->Command[0]))
+ {
+ Arc.Seek(0,SEEK_SET);
+ }
+ else
+#endif
+ break;
+ }
+ else
+ {
+ if (Cmd->ArcNames.ItemsCount()<2 && !Bare)
+ mprintf(St(MNotRAR),Arc.FileName);
+ break;
+ }
+ }
+ }
+
+ // Clean user entered password. Not really required, just for extra safety.
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean();
+
+ if (ArcCount>1 && !Bare && !Technical)
+ {
+ wchar UnpSizeText[20],PackSizeText[20];
+ itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText));
+ itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText));
+
+ if (Verbose)
+ mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText,
+ ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount);
+ else
+ mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount);
+ }
+}
+
+
+enum LISTCOL_TYPE {
+ LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR
+};
+
+
+void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare)
+{
+ wchar *Name=hd.FileName;
+ RARFORMAT Format=Arc.Format;
+
+ if (Bare)
+ {
+ mprintf(L"%s\n",Name);
+ return;
+ }
+
+ if (!TitleShown && !Technical)
+ {
+ if (Verbose)
+ {
+ mprintf(L"\n%ls",St(MListTitleV));
+ mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----");
+ }
+ else
+ {
+ mprintf(L"\n%ls",St(MListTitleL));
+ mprintf(L"\n----------- --------- ---------- ----- ----");
+ }
+ TitleShown=true;
+ }
+
+ wchar UnpSizeText[30],PackSizeText[30];
+ if (hd.UnpSize==INT64NDF)
+ wcscpy(UnpSizeText,L"?");
+ else
+ itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
+ itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
+
+ wchar AttrStr[30];
+ if (hd.HeaderType==HEAD_SERVICE)
+ swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.');
+ else
+ ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr));
+
+ wchar RatioStr[10];
+
+ if (hd.SplitBefore && hd.SplitAfter)
+ wcscpy(RatioStr,L"<->");
+ else
+ if (hd.SplitBefore)
+ wcscpy(RatioStr,L"<--");
+ else
+ if (hd.SplitAfter)
+ wcscpy(RatioStr,L"-->");
+ else
+ swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
+
+ wchar DateStr[50];
+ hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical);
+
+ if (Technical)
+ {
+ mprintf(L"\n%12s: %s",St(MListName),Name);
+
+ bool FileBlock=hd.HeaderType==HEAD_FILE;
+
+ if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
+ {
+ mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream));
+ wchar StreamName[NM];
+ GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName));
+ mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName);
+ }
+ else
+ {
+ const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService);
+
+ if (hd.RedirType!=FSREDIR_NONE)
+ switch(hd.RedirType)
+ {
+ case FSREDIR_UNIXSYMLINK:
+ Type=St(MListUSymlink); break;
+ case FSREDIR_WINSYMLINK:
+ Type=St(MListWSymlink); break;
+ case FSREDIR_JUNCTION:
+ Type=St(MListJunction); break;
+ case FSREDIR_HARDLINK:
+ Type=St(MListHardlink); break;
+ case FSREDIR_FILECOPY:
+ Type=St(MListCopy); break;
+ }
+ mprintf(L"\n%12ls: %ls",St(MListType),Type);
+ if (hd.RedirType!=FSREDIR_NONE)
+ if (Format==RARFMT15)
+ {
+ char LinkTargetA[NM];
+ if (Arc.FileHead.Encrypted)
+ {
+ // Link data are encrypted. We would need to ask for password
+ // and initialize decryption routine to display the link target.
+ strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA));
+ }
+ else
+ {
+ int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1);
+ Arc.Read(LinkTargetA,DataSize);
+ LinkTargetA[DataSize > 0 ? DataSize : 0] = 0;
+ }
+ wchar LinkTarget[NM];
+ CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget));
+ mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget);
+ }
+ else
+ mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName);
+ }
+ if (!hd.Dir)
+ {
+ mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText);
+ mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText);
+ mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr);
+ }
+ if (hd.mtime.IsSet())
+ mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr);
+ if (hd.ctime.IsSet())
+ {
+ hd.ctime.GetText(DateStr,ASIZE(DateStr),true);
+ mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr);
+ }
+ if (hd.atime.IsSet())
+ {
+ hd.atime.GetText(DateStr,ASIZE(DateStr),true);
+ mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr);
+ }
+ mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr);
+ if (hd.FileHash.Type==HASH_CRC32)
+ mprintf(L"\n%12ls: %8.8X",
+ hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32",
+ hd.FileHash.CRC32);
+ if (hd.FileHash.Type==HASH_BLAKE2)
+ {
+ wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1];
+ BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr));
+ mprintf(L"\n%12ls: %ls",
+ hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2",
+ BlakeStr);
+ }
+
+ const wchar *HostOS=L"";
+ if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN)
+ HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix";
+ if (Format==RARFMT15)
+ {
+ static const wchar *RarOS[]={
+ L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L""
+ };
+ if (hd.HostOS=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400,
+ hd.WinSize>=0x100000 ? L"M":L"K");
+
+ if (hd.Solid || hd.Encrypted)
+ {
+ mprintf(L"\n%12ls: ",St(MListFlags));
+ if (hd.Solid)
+ mprintf(L"%ls ",St(MListSolid));
+ if (hd.Encrypted)
+ mprintf(L"%ls ",St(MListEnc));
+ }
+
+ if (hd.Version)
+ {
+ uint Version=ParseVersionFileName(Name,false);
+ if (Version!=0)
+ mprintf(L"\n%12ls: %u",St(MListFileVer),Version);
+ }
+
+ if (hd.UnixOwnerSet)
+ {
+ mprintf(L"\n%12ls: ",L"Unix owner");
+ if (*hd.UnixOwnerName!=0)
+ mprintf(L"%ls:",GetWide(hd.UnixOwnerName));
+ if (*hd.UnixGroupName!=0)
+ mprintf(L"%ls",GetWide(hd.UnixGroupName));
+ if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric))
+ mprintf(L" ");
+ if (hd.UnixOwnerNumeric)
+ mprintf(L"#%d:",hd.UnixOwnerID);
+ if (hd.UnixGroupNumeric)
+ mprintf(L"#%d:",hd.UnixGroupID);
+ }
+
+ mprintf(L"\n");
+ return;
+ }
+
+ mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText);
+
+ if (Verbose)
+ mprintf(L"%9ls %4ls ",PackSizeText,RatioStr);
+
+ mprintf(L" %ls ",DateStr);
+
+ if (Verbose)
+ {
+ if (hd.FileHash.Type==HASH_CRC32)
+ mprintf(L"%8.8X ",hd.FileHash.CRC32);
+ else
+ if (hd.FileHash.Type==HASH_BLAKE2)
+ {
+ byte *S=hd.FileHash.Digest;
+ mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]);
+ }
+ else
+ mprintf(L"???????? ");
+ }
+ mprintf(L"%ls",Name);
+}
+
+/*
+void ListSymLink(Archive &Arc)
+{
+ if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000)
+ if (Arc.FileHead.Encrypted)
+ {
+ // Link data are encrypted. We would need to ask for password
+ // and initialize decryption routine to display the link target.
+ mprintf(L"\n%22ls %ls",L"-->",L"*<-?->");
+ }
+ else
+ {
+ char FileName[NM];
+ uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1);
+ Arc.Read(FileName,DataSize);
+ FileName[DataSize]=0;
+ mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName));
+ }
+}
+*/
+
+void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize)
+{
+ switch(HostType)
+ {
+ case HSYS_WINDOWS:
+ swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c",
+ (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed.
+ (A & 0x0800)!=0 ? 'C' : '.', // Compressed.
+ (A & 0x0020)!=0 ? 'A' : '.', // Archive.
+ (A & 0x0010)!=0 ? 'D' : '.', // Directory.
+ (A & 0x0004)!=0 ? 'S' : '.', // System.
+ (A & 0x0002)!=0 ? 'H' : '.', // Hidden.
+ (A & 0x0001)!=0 ? 'R' : '.'); // Read-only.
+ break;
+ case HSYS_UNIX:
+ switch (A & 0xF000)
+ {
+ case 0x4000:
+ AttrStr[0]='d';
+ break;
+ case 0xA000:
+ AttrStr[0]='l';
+ break;
+ default:
+ AttrStr[0]='-';
+ break;
+ }
+ swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c",
+ (A & 0x0100) ? 'r' : '-',
+ (A & 0x0080) ? 'w' : '-',
+ (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'),
+ (A & 0x0020) ? 'r' : '-',
+ (A & 0x0010) ? 'w' : '-',
+ (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'),
+ (A & 0x0004) ? 'r' : '-',
+ (A & 0x0002) ? 'w' : '-',
+ (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
+ break;
+ case HSYS_UNKNOWN:
+ wcscpy(AttrStr,L"?");
+ break;
+ }
+}
diff --git a/list.hpp b/list.hpp
new file mode 100644
index 0000000..7721ae5
--- /dev/null
+++ b/list.hpp
@@ -0,0 +1,6 @@
+#ifndef _RAR_LIST_
+#define _RAR_LIST_
+
+void ListArchive(CommandData *Cmd);
+
+#endif
diff --git a/loclang.hpp b/loclang.hpp
new file mode 100644
index 0000000..077be72
--- /dev/null
+++ b/loclang.hpp
@@ -0,0 +1,384 @@
+#define MYesNo L"_Yes_No"
+#define MYesNoAll L"_Yes_No_All"
+#define MYesNoAllQ L"_Yes_No_All_nEver_Quit"
+#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit"
+#define MContinueQuit L"_Continue_Quit"
+#define MRetryAbort L"_Retry_Abort"
+#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d"
+#define MRegTo L"\nRegistered to %s\n"
+#define MShare L"\nTrial version Type RAR -? for help\n"
+#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n"
+#define MBeta L"beta"
+#define MMonthJan L"Jan"
+#define MMonthFeb L"Feb"
+#define MMonthMar L"Mar"
+#define MMonthApr L"Apr"
+#define MMonthMay L"May"
+#define MMonthJun L"Jun"
+#define MMonthJul L"Jul"
+#define MMonthAug L"Aug"
+#define MMonthSep L"Sep"
+#define MMonthOct L"Oct"
+#define MMonthNov L"Nov"
+#define MMonthDec L"Dec"
+#define MRARTitle1 L"\nUsage: rar - -"
+#define MUNRARTitle1 L"\nUsage: unrar - -"
+#define MRARTitle2 L"\n <@listfiles...> "
+#define MCHelpCmd L"\n\n"
+#define MCHelpCmdA L"\n a Add files to archive"
+#define MCHelpCmdC L"\n c Add archive comment"
+#define MCHelpCmdCH L"\n ch Change archive parameters"
+#define MCHelpCmdCW L"\n cw Write archive comment to file"
+#define MCHelpCmdD L"\n d Delete files from archive"
+#define MCHelpCmdE L"\n e Extract files without archived paths"
+#define MCHelpCmdF L"\n f Freshen files in archive"
+#define MCHelpCmdI L"\n i[par]= Find string in archives"
+#define MCHelpCmdK L"\n k Lock archive"
+#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]"
+#define MCHelpCmdM L"\n m[f] Move to archive [files only]"
+#define MCHelpCmdP L"\n p Print file to stdout"
+#define MCHelpCmdR L"\n r Repair archive"
+#define MCHelpCmdRC L"\n rc Reconstruct missing volumes"
+#define MCHelpCmdRN L"\n rn Rename archived files"
+#define MCHelpCmdRR L"\n rr[N] Add data recovery record"
+#define MCHelpCmdRV L"\n rv[N] Create recovery volumes"
+#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX"
+#define MCHelpCmdT L"\n t Test archive files"
+#define MCHelpCmdU L"\n u Update files in archive"
+#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]"
+#define MCHelpCmdX L"\n x Extract files with full path"
+#define MCHelpSw L"\n\n"
+#define MCHelpSwm L"\n - Stop switches scanning"
+#define MCHelpSwAT L"\n @[+] Disable [enable] file lists"
+#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction"
+#define MCHelpSwAD L"\n ad Append archive name to destination path"
+#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date"
+#define MCHelpSwAI L"\n ai Ignore file attributes"
+#define MCHelpSwAO L"\n ao Add files with Archive attribute set"
+#define MCHelpSwAP L"\n ap Set path inside archive"
+#define MCHelpSwAS L"\n as Synchronize archive contents"
+#define MCHelpSwCm L"\n c- Disable comments show"
+#define MCHelpSwCFGm L"\n cfg- Disable read configuration"
+#define MCHelpSwCL L"\n cl Convert names to lower case"
+#define MCHelpSwCU L"\n cu Convert names to upper case"
+#define MCHelpSwDF L"\n df Delete files after archiving"
+#define MCHelpSwDH L"\n dh Open shared files"
+#define MCHelpSwDR L"\n dr Delete files to Recycle Bin"
+#define MCHelpSwDS L"\n ds Disable name sort for solid archive"
+#define MCHelpSwDW L"\n dw Wipe files after archiving"
+#define MCHelpSwEa L"\n e[+] Set file exclude and include attributes"
+#define MCHelpSwED L"\n ed Do not add empty directories"
+#define MCHelpSwEN L"\n en Do not put 'end of archive' block"
+#define MCHelpSwEP L"\n ep Exclude paths from names"
+#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names"
+#define MCHelpSwEP2 L"\n ep2 Expand paths to full"
+#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter"
+#define MCHelpSwF L"\n f Freshen files"
+#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers"
+#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum"
+#define MCHelpSwIDP L"\n id[c,d,p,q] Disable messages"
+#define MCHelpSwIEML L"\n ieml[addr] Send archive by email"
+#define MCHelpSwIERR L"\n ierr Send all messages to stderr"
+#define MCHelpSwILOG L"\n ilog[name] Log errors to file (registered versions only)"
+#define MCHelpSwINUL L"\n inul Disable all messages"
+#define MCHelpSwIOFF L"\n ioff Turn PC off after completing an operation"
+#define MCHelpSwISND L"\n isnd Enable sound"
+#define MCHelpSwK L"\n k Lock archive"
+#define MCHelpSwKB L"\n kb Keep broken extracted files"
+#define MCHelpSwLog L"\n log[f][=name] Write names to log file"
+#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)"
+#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format"
+#define MCHelpSwMC L"\n mc Set advanced compression parameters"
+#define MCHelpSwMD L"\n md[k,m,g] Dictionary size in KB, MB or GB"
+#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store"
+#define MCHelpSwMT L"\n mt Set the number of threads"
+#define MCHelpSwN L"\n n Additionally filter included files"
+#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin"
+#define MCHelpSwNal L"\n n@ Read additional filter masks from list file"
+#define MCHelpSwO L"\n o[+|-] Set the overwrite mode"
+#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute"
+#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file"
+#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references"
+#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]"
+#define MCHelpSwOR L"\n or Rename files automatically"
+#define MCHelpSwOS L"\n os Save NTFS streams"
+#define MCHelpSwOW L"\n ow Save or restore file owner and group"
+#define MCHelpSwP L"\n p[password] Set password"
+#define MCHelpSwPm L"\n p- Do not query password"
+#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]"
+#define MCHelpSwR L"\n r Recurse subdirectories"
+#define MCHelpSwRm L"\n r- Disable recursion"
+#define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only"
+#define MCHelpSwRI L"\n ri
[:] Set priority (0-default,1-min..15-max) and sleep time in ms"
+#define MCHelpSwRR L"\n rr[N] Add data recovery record"
+#define MCHelpSwRV L"\n rv[N] Create recovery volumes"
+#define MCHelpSwS L"\n s[,v[-],e] Create solid archive"
+#define MCHelpSwSm L"\n s- Disable solid archiving"
+#define MCHelpSwSC L"\n sc[obj] Specify the character set"
+#define MCHelpSwSFX L"\n sfx[name] Create SFX archive"
+#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)"
+#define MCHelpSwSL L"\n sl Process files with size less than specified"
+#define MCHelpSwSM L"\n sm Process files with size more than specified"
+#define MCHelpSwT L"\n t Test files after archiving"
+#define MCHelpSwTK L"\n tk Keep original archive time"
+#define MCHelpSwTL L"\n tl Set archive time to latest file"
+#define MCHelpSwTN L"\n tn