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 MCHelpSwONI L"\n oni Allow potentially incompatible names"
+#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 Process files newer than "
+#define MCHelpSwTO L"\n to Process files older than "
+#define MCHelpSwTA L"\n ta Process files modified after in YYYYMMDDHHMMSS format"
+#define MCHelpSwTB L"\n tb Process files modified before in YYYYMMDDHHMMSS format"
+#define MCHelpSwTS L"\n ts[m|c|a] Save or restore file time (modification, creation, access)"
+#define MCHelpSwU L"\n u Update files"
+#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes"
+#define MCHelpSwVUnr L"\n v List all volumes"
+#define MCHelpSwVn L"\n v[k,b] Create volumes with size=*1000 [*1024, *1]"
+#define MCHelpSwVD L"\n vd Erase disk contents before creating volume"
+#define MCHelpSwVER L"\n ver[n] File version control"
+#define MCHelpSwVN L"\n vn Use the old style volume naming scheme"
+#define MCHelpSwVP L"\n vp Pause before each volume"
+#define MCHelpSwW L"\n w Assign work directory"
+#define MCHelpSwX L"\n x Exclude specified file"
+#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin"
+#define MCHelpSwXal L"\n x@ Exclude files listed in specified list file"
+#define MCHelpSwY L"\n y Assume Yes on all queries"
+#define MCHelpSwZ L"\n z[file] Read archive comment from file"
+#define MBadArc L"\nERROR: Bad archive %s\n"
+#define MAskPsw L"Enter password (will not be echoed)"
+#define MAskPswFor L"\nEnter password (will not be echoed) for %s: "
+#define MReAskPsw L"\nReenter password: "
+#define MNotMatchPsw L"\nERROR: Passwords do not match\n"
+#define MErrWrite L"Write error in the file %s"
+#define MErrRead L"Read error in the file %s"
+#define MErrSeek L"Seek error in the file %s"
+#define MErrFClose L"Cannot close the file %s"
+#define MErrOutMem L"Not enough memory"
+#define MErrBrokenArc L"Corrupt archive - use 'Repair' command"
+#define MProgAborted L"Program aborted"
+#define MErrRename L"\nCannot rename %s to %s"
+#define MAbsNextVol L"\nCannot find volume %s"
+#define MBreak L"\nUser break\n"
+#define MAskCreatVol L"\nCreate next volume ?"
+#define MAskNextDisk L"\nDisk full. Insert next"
+#define MCreatVol L"\n\nCreating %sarchive %s\n"
+#define MAskNextVol L"\nInsert disk with %s"
+#define MTestVol L"\n\nTesting archive %s\n"
+#define MExtrVol L"\n\nExtracting from %s\n"
+#define MConverting L"\nConverting %s"
+#define MCvtToSFX L"\nConvert archives to SFX"
+#define MCvtFromSFX L"\nRemoving SFX module"
+#define MNotSFX L"\n%s is not SFX archive"
+#define MNotRAR L"\n%s is not RAR archive"
+#define MNotFirstVol L"\n%s is not the first volume"
+#define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format"
+#define MCannotCreate L"\nCannot create %s"
+#define MCannotOpen L"\nCannot open %s"
+#define MUnknownMeth L"\nUnknown method in %s"
+#define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version."
+#define MOk L" OK"
+#define MDone L"\nDone"
+#define MLockingArc L"\nLocking archive"
+#define MNotMdfOld L"\n\nERROR: Cannot modify old format archive"
+#define MNotMdfLock L"\n\nERROR: Locked archive"
+#define MNotMdfVol L"\n\nERROR: Cannot modify volume"
+#define MPackAskReg L"\nEvaluation copy. Please register.\n"
+#define MCreateArchive L"\nCreating %sarchive %s\n"
+#define MUpdateArchive L"\nUpdating %sarchive %s\n"
+#define MAddSolid L"solid "
+#define MAddFile L"\nAdding %-58s "
+#define MUpdFile L"\nUpdating %-58s "
+#define MAddPoints L"\n... %-58s "
+#define MMoveDelFiles L"\n\nDeleting files %s..."
+#define MMoveDelDirs L"and directories"
+#define MMoveDelFile L"\nDeleting %-30s"
+#define MMoveDeleted L" deleted"
+#define MMoveNotDeleted L" NOT DELETED"
+#define MClearAttrib L"\n\nClearing attributes..."
+#define MMoveDelDir L"\nDeleting directory %-30s"
+#define MWarErrFOpen L"\nWARNING: Cannot open %d %s"
+#define MErrOpenFiles L"files"
+#define MErrOpenFile L"file"
+#define MAddNoFiles L"\nWARNING: No files"
+#define MMdfEncrSol L"\n%s: encrypted"
+#define MCannotMdfEncrSol L"\nCannot modify solid archive containing encrypted files"
+#define MAddAnalyze L"\nAnalyzing archived files: "
+#define MRepacking L"\nRepacking archived files: "
+#define MCRCFailed L"\n%-20s - checksum error"
+#define MExtrTest L"\n\nTesting archive %s\n"
+#define MExtracting L"\n\nExtracting from %s\n"
+#define MUseCurPsw L"\n%s - use current password ?"
+#define MCreatDir L"\nCreating %-56s"
+#define MExtrSkipFile L"\nSkipping %-56s"
+#define MExtrTestFile L"\nTesting %-56s"
+#define MExtrFile L"\nExtracting %-56s"
+#define MExtrPoints L"\n... %-56s"
+#define MExtrErrMkDir L"\nCannot create directory %s"
+#define MExtrPrinting L"\n------ Printing %s\n\n"
+#define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password."
+#define MExtrNoFiles L"\nNo files to extract"
+#define MExtrAllOk L"\nAll OK"
+#define MExtrTotalErr L"\nTotal errors: %ld"
+#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n"
+#define MAskOverwrite L"\nOverwrite %s ?"
+#define MAskNewName L"\nEnter new name: "
+#define MHeaderBroken L"\nCorrupt header is found"
+#define MMainHeaderBroken L"\nMain archive header is corrupt"
+#define MLogFileHead L"\n%s - the file header is corrupt"
+#define MLogProtectHead L"The data recovery header is corrupt"
+#define MReadStdinCmt L"\nReading comment from stdin\n"
+#define MReadCommFrom L"\nReading comment from %s"
+#define MDelComment L"\nDeleting comment from %s"
+#define MAddComment L"\nAdding comment to %s"
+#define MFCommAdd L"\nAdding file comments"
+#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n"
+#define MLogCommBrk L"\nThe archive comment is corrupt"
+#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:"
+#define MWriteCommTo L"\nWrite comment to %s"
+#define MCommNotPres L"\nComment is not present"
+#define MDelFrom L"\nDeleting from %s"
+#define MDeleting L"\nDeleting %s"
+#define MEraseArc L"\nErasing empty archive %s"
+#define MNoDelFiles L"\nNo files to delete"
+#define MLogTitle L"-------- %2d %s %d, archive %s"
+#define MPathTooLong L"\nERROR: Path too long\n"
+#define MListArchive L"Archive"
+#define MListDetails L"Details"
+#define MListSolid L"solid"
+#define MListSFX L"SFX"
+#define MListVolume L"volume"
+#define MListRR L"recovery record"
+#define MListLock L"lock"
+#define MListEnc L"encrypted"
+#define MListEncHead L"encrypted headers"
+#define MListTitleL L" Attributes Size Date Time Name"
+#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name"
+#define MListName L"Name"
+#define MListType L"Type"
+#define MListFile L"File"
+#define MListDir L"Directory"
+#define MListUSymlink L"Unix symbolic link"
+#define MListWSymlink L"Windows symbolic link"
+#define MListJunction L"NTFS junction point"
+#define MListHardlink L"Hard link"
+#define MListCopy L"File reference"
+#define MListStream L"NTFS alternate data stream"
+#define MListTarget L"Target"
+#define MListSize L"Size"
+#define MListPacked L"Packed size"
+#define MListRatio L"Ratio"
+#define MListMtime L"mtime"
+#define MListCtime L"ctime"
+#define MListAtime L"atime"
+#define MListAttr L"Attributes"
+#define MListFlags L"Flags"
+#define MListCompInfo L"Compression"
+#define MListHostOS L"Host OS"
+#define MListFileVer L"File version"
+#define MListService L"Service"
+#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s"
+#define MListNTACLHead L"\n NTFS security data"
+#define MListStrmHead L"\n NTFS stream: %s"
+#define MListUnkHead L"\n Unknown subheader type: 0x%04x"
+#define MFileComment L"\nComment: "
+#define MYes L"Yes"
+#define MNo L"No"
+#define MListNoFiles L" 0 files\n"
+#define MRprReconstr L"\nReconstructing %s"
+#define MRprBuild L"\nBuilding %s"
+#define MRprOldFormat L"\nCannot repair archive with old format"
+#define MRprFind L"\nFound %s"
+#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?"
+#define MRprNoFiles L"\nNo files found"
+#define MLogUnexpEOF L"\nUnexpected end of archive"
+#define MRepAskReconst L"\nReconstruct archive structure ?"
+#define MRecScanning L"\nScanning..."
+#define MRecRNotFound L"\nData recovery record not found"
+#define MRecRFound L"\nData recovery record found"
+#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged"
+#define MRecCorrected L" - data recovered"
+#define MRecFailed L" - cannot recover data"
+#define MAddRecRec L"\nAdding data recovery record"
+#define MEraseForVolume L"\n\nErasing contents of drive %c:\n"
+#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n"
+#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n"
+#define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n"
+#define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n"
+#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n"
+#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s"
+#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists"
+#define MAskRetryCreate L"\nCannot create %s. Retry ?"
+#define MListMACHead1 L"\n Mac OS file type: %c%c%c%c ; "
+#define MListMACHead2 L"file creator: %c%c%c%c\n"
+#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s"
+#define MFileRO L"\n%s is read-only"
+#define MACLGetError L"\nWARNING: Cannot get %s security data\n"
+#define MACLSetError L"\nWARNING: Cannot set %s security data\n"
+#define MACLBroken L"\nERROR: %s security data are corrupt\n"
+#define MACLUnknown L"\nWARNING: Unknown format of %s security data\n"
+#define MStreamBroken L"\nERROR: %s stream data are corrupt\n"
+#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n"
+#define MInvalidName L"\nERROR: Invalid file name %s"
+#define MProcessArc L"\n\nProcessing archive %s"
+#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file name"
+#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s"
+#define MUnknownOption L"\nERROR: Unknown option: %s"
+#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored"
+#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored"
+#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block"
+#define MSubHeadType L"\nData header type: %s"
+#define MScanError L"\nCannot read contents of %s"
+#define MNotVolume L"\n%s is not volume"
+#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets"
+#define MRecVolMissing L"\n%d volumes missing"
+#define MRecVolFound L"\n%d recovery volumes found"
+#define MRecVolAllExist L"\nNothing to reconstruct"
+#define MRecVolCannotFix L"\nReconstruction impossible"
+#define MReconstructing L"\nReconstructing..."
+#define MCreating L"\nCreating %s"
+#define MRenaming L"\nRenaming %s to %s"
+#define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB"
+#define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files"
+#define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s"
+#define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives"
+#define MCannotEncName L"\nCannot encrypt archive already containing encrypted files"
+#define MCannotEmail L"\nCannot email the file %s"
+#define MCopyrightS L"\nRAR SFX archive"
+#define MSHelpCmd L"\n\n"
+#define MSHelpCmdE L"\n -x Extract from archive (default)"
+#define MSHelpCmdT L"\n -t Test archive files"
+#define MSHelpCmdV L"\n -v Verbosely list contents of archive"
+#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d"
+#define MVolumeNumber L"volume %d"
+#define MCannotDelete L"\nCannot delete %s"
+#define MCalcCRC L"\nCalculating the checksum"
+#define MTooLargeSFXArc L"\nWARNING: Too large SFX archive. Windows cannot run the executable file exceeding 4 GB."
+#define MCalcCRCAllVol L"\nCalculating checksums of all volumes."
+#define MNotEnoughDisk L"\nERROR: Not enough disk space for %s."
+#define MNewerRAR L"\nYou may need a newer version of RAR."
+#define MUnkEncMethod L"\nUnknown encryption method in %s"
+#define MWrongPassword L"\nThe specified password is incorrect."
+#define MRepairing L"\nRepairing"
+#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x"
+#define MBlocksRecovered L"\n%d blocks recovered."
+#define MRRDamaged L"\nRecovery record is corrupt."
+#define MTestingRR L"\nTesting the recovery record"
+#define MFailed L"Failed"
+#define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format."
+#define MSearchDupFiles L"\nSearching for identical files"
+#define MNumFound L"%d found."
+#define MUnknownExtra L"\nUnknown extra field in %s."
+#define MCorruptExtra L"\nCorrupt %s extra field in %s."
+#define MCopyError L"\nCannot copy %s to %s."
+#define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries."
+#define MCopyingData L"\nCopying data"
+#define MErrCreateLnkS L"\nCannot create symbolic link %s"
+#define MErrCreateLnkH L"\nCannot create hard link %s"
+#define MNeedAdmin L"\nYou may need to run RAR as administrator"
+#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB."
+#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary."
diff --git a/reference/unrar/log.cpp b/reference/unrar/log.cpp
new file mode 100644
index 00000000..8bbe8ee0
--- /dev/null
+++ b/reference/unrar/log.cpp
@@ -0,0 +1,37 @@
+#include "rar.hpp"
+
+
+static wchar LogName[NM];
+static RAR_CHARSET LogCharset=RCH_DEFAULT;
+
+void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet)
+{
+ wcsncpyz(LogName,LogFileName,ASIZE(LogName));
+ LogCharset=CSet;
+}
+
+
+#ifndef SILENT
+void Log(const wchar *ArcName,const wchar *fmt,...)
+{
+ // Preserve the error code for possible following system error message.
+ int Code=ErrHandler.GetSystemErrorCode();
+
+ uiAlarm(UIALARM_ERROR);
+
+ // 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));
+
+ safebuf wchar Msg[2*NM+1024];
+ va_list arglist;
+ va_start(arglist,fmt);
+ vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
+ va_end(arglist);
+ eprintf(L"%ls",Msg);
+ ErrHandler.SetSystemErrorCode(Code);
+}
+#endif
+
+
diff --git a/reference/unrar/log.hpp b/reference/unrar/log.hpp
new file mode 100644
index 00000000..008ef11a
--- /dev/null
+++ b/reference/unrar/log.hpp
@@ -0,0 +1,12 @@
+#ifndef _RAR_LOG_
+#define _RAR_LOG_
+
+void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet);
+
+#ifdef SILENT
+inline void Log(const wchar *ArcName,const wchar *fmt,...) {}
+#else
+void Log(const wchar *ArcName,const wchar *fmt,...);
+#endif
+
+#endif
diff --git a/reference/unrar/makefile b/reference/unrar/makefile
new file mode 100644
index 00000000..4f46c2a9
--- /dev/null
+++ b/reference/unrar/makefile
@@ -0,0 +1,173 @@
+#
+# Makefile for UNIX - unrar
+
+# Linux using GCC
+CXX=c++
+CXXFLAGS=-O2
+LIBFLAGS=-fPIC
+DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
+STRIP=strip
+AR=ar
+LDFLAGS=-pthread
+DESTDIR=/usr
+
+# Linux using LCC
+#CXX=lcc
+#CXXFLAGS=-O2
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# CYGWIN using GCC
+#CXX=c++
+#CXXFLAGS=-O2
+#LIBFLAGS=
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP
+#STRIP=strip
+#AR=ar
+#LDFLAGS=-pthread
+#DESTDIR=/usr
+
+# HP UX using aCC
+#CXX=aCC
+#CXXFLAGS=-AA +O2 +Onolimit
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# IRIX using GCC
+#CXX=g++
+#CXXFLAGS=-O2
+#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# IRIX using MIPSPro (experimental)
+#CXX=CC
+#CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std
+#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# AIX using xlC (IBM VisualAge C++ 5.0)
+#CXX=xlC
+#CXXFLAGS=-O -qinline -qro -qroconst -qmaxmem=16384 -qcpluscmt
+#DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API
+#LIBS=-lbsd
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# Solaris using CC
+#CXX=CC
+#CXXFLAGS=-fast -erroff=wvarhidemem
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#STRIP=strip
+#AR=ar
+#DESTDIR=/usr
+
+# Solaris using GCC (optimized for UltraSPARC 1 CPU)
+#CXX=g++
+#CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#STRIP=/usr/ccs/bin/strip
+#AR=/usr/ccs/bin/ar
+#DESTDIR=/usr
+
+# Tru64 5.1B using GCC3
+#CXX=g++
+#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500
+#STRIP=strip
+#AR=ar
+#LDFLAGS=-rpath /usr/local/gcc/lib
+#DESTDIR=/usr
+
+# Tru64 5.1B using DEC C++
+#CXX=cxx
+#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long
+#STRIP=strip
+#AR=ar
+#LDFLAGS=
+#DESTDIR=/usr
+
+# QNX 6.x using GCC
+#CXX=g++
+#CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions
+#STRIP=strip
+#AR=ar
+#LDFLAGS=-fexceptions
+#DESTDIR=/usr
+
+# Cross-compile
+# Linux using arm-linux-g++
+#CXX=arm-linux-g++
+#CXXFLAGS=-O2
+#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+#STRIP=arm-linux-strip
+#AR=arm-linux-ar
+#LDFLAGS=-static
+#DESTDIR=/usr
+
+##########################
+
+COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES)
+LINK=$(CXX)
+
+WHAT=UNRAR
+
+UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o
+LIB_OBJ=filestr.o scantree.o dll.o qopen.o
+
+OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \
+ archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \
+ resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \
+ rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \
+ list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o
+
+.cpp.o:
+ $(COMPILE) -D$(WHAT) -c $<
+
+all: unrar
+
+install: install-unrar
+
+uninstall: uninstall-unrar
+
+clean:
+ @rm -f *.o *.bak *~
+
+unrar: clean $(OBJECTS) $(UNRAR_OBJ)
+ @rm -f unrar
+ $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
+ $(STRIP) unrar
+
+sfx: WHAT=SFX_MODULE
+sfx: clean $(OBJECTS)
+ @rm -f default.sfx
+ $(LINK) -o default.sfx $(LDFLAGS) $(OBJECTS)
+ $(STRIP) default.sfx
+
+lib: WHAT=RARDLL
+lib: CXXFLAGS+=$(LIBFLAGS)
+lib: clean $(OBJECTS) $(LIB_OBJ)
+ @rm -f libunrar.so
+ @rm -f libunrar.a
+ $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ)
+ $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ)
+
+install-unrar:
+ install -D unrar $(DESTDIR)/bin/unrar
+
+uninstall-unrar:
+ rm -f $(DESTDIR)/bin/unrar
+
+install-lib:
+ install libunrar.so $(DESTDIR)/lib
+ install libunrar.a $(DESTDIR)/lib
+
+uninstall-lib:
+ rm -f $(DESTDIR)/lib/libunrar.so
diff --git a/reference/unrar/match.cpp b/reference/unrar/match.cpp
new file mode 100644
index 00000000..4369a578
--- /dev/null
+++ b/reference/unrar/match.cpp
@@ -0,0 +1,145 @@
+#include "rar.hpp"
+
+static bool match(const wchar *pattern,const wchar *string,bool ForceCase);
+static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase);
+static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase);
+
+inline uint touppercw(uint ch,bool ForceCase)
+{
+ if (ForceCase)
+ return ch;
+#if defined(_UNIX)
+ return ch;
+#else
+ return toupperw(ch);
+#endif
+}
+
+
+bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
+{
+ bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0;
+
+ CmpMode&=MATCH_MODEMASK;
+
+ if (CmpMode!=MATCH_NAMES)
+ {
+ size_t WildLength=wcslen(Wildcard);
+ if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH &&
+ mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0)
+ {
+ // For all modes except MATCH_NAMES, MATCH_EXACT and MATCH_EXACTPATH
+ // "path1" mask must match "path1\path2\filename.ext" and "path1" names.
+ wchar NextCh=Name[WildLength];
+ if (NextCh==L'\\' || NextCh==L'/' || NextCh==0)
+ return(true);
+ }
+
+ // Nothing more to compare for MATCH_SUBPATHONLY.
+ if (CmpMode==MATCH_SUBPATHONLY)
+ return(false);
+
+ wchar Path1[NM],Path2[NM];
+ GetFilePath(Wildcard,Path1,ASIZE(Path1));
+ GetFilePath(Name,Path2,ASIZE(Path2));
+
+ if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
+ mwcsicompc(Path1,Path2,ForceCase)!=0)
+ return(false);
+ if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH)
+ if (IsWildcard(Path1))
+ return(match(Wildcard,Name,ForceCase));
+ else
+ if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard))
+ {
+ if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0)
+ return(false);
+ }
+ else
+ if (mwcsicompc(Path1,Path2,ForceCase)!=0)
+ return(false);
+ }
+ wchar *Name1=PointToName(Wildcard);
+ wchar *Name2=PointToName(Name);
+
+ // Always return false for RAR temporary files to exclude them
+ // from archiving operations.
+ if (mwcsnicompc(L"__rar_",Name2,6,false)==0)
+ return(false);
+
+ if (CmpMode==MATCH_EXACT)
+ return(mwcsicompc(Name1,Name2,ForceCase)==0);
+
+ return(match(Name1,Name2,ForceCase));
+}
+
+
+bool match(const wchar *pattern,const wchar *string,bool ForceCase)
+{
+ for (;; ++string)
+ {
+ wchar stringc=touppercw(*string,ForceCase);
+ wchar patternc=touppercw(*pattern++,ForceCase);
+ switch (patternc)
+ {
+ case 0:
+ return(stringc==0);
+ case '?':
+ if (stringc == 0)
+ return(false);
+ break;
+ case '*':
+ if (*pattern==0)
+ return(true);
+ if (*pattern=='.')
+ {
+ if (pattern[1]=='*' && pattern[2]==0)
+ return(true);
+ const wchar *dot=wcschr(string,'.');
+ if (pattern[1]==0)
+ return (dot==NULL || dot[1]==0);
+ if (dot!=NULL)
+ {
+ string=dot;
+ if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL)
+ return(mwcsicompc(pattern+1,string+1,ForceCase)==0);
+ }
+ }
+
+ while (*string)
+ if (match(pattern,string++,ForceCase))
+ return(true);
+ return(false);
+ default:
+ if (patternc != stringc)
+ {
+ // Allow "name." mask match "name" and "name.\" match "name\".
+ if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.'))
+ return(match(pattern,string,ForceCase));
+ else
+ return(false);
+ }
+ break;
+ }
+ }
+}
+
+
+int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase)
+{
+ if (ForceCase)
+ return wcscmp(Str1,Str2);
+ return wcsicompc(Str1,Str2);
+}
+
+
+int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase)
+{
+ if (ForceCase)
+ return wcsncmp(Str1,Str2,N);
+#if defined(_UNIX)
+ return wcsncmp(Str1,Str2,N);
+#else
+ return wcsnicomp(Str1,Str2,N);
+#endif
+}
diff --git a/reference/unrar/match.hpp b/reference/unrar/match.hpp
new file mode 100644
index 00000000..65493ff2
--- /dev/null
+++ b/reference/unrar/match.hpp
@@ -0,0 +1,34 @@
+#ifndef _RAR_MATCH_
+#define _RAR_MATCH_
+
+enum {
+ MATCH_NAMES, // Paths are ignored.
+ // Compares names only using wildcards.
+
+ MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard
+ // must be present in the beginning of file path.
+ // For example, "c:\path1\*" or "c:\path1" will match
+ // "c:\path1\path2\file".
+ // Names are not compared.
+
+ MATCH_EXACT, // Paths must match exactly.
+ // Names must match exactly.
+
+ MATCH_EXACTPATH, // Paths must match exactly.
+ // Names are compared using wildcards.
+
+ MATCH_SUBPATH, // Names must be the same, but path in mask is allowed
+ // to be only a part of name path. In other words,
+ // we match all files matching the file mask
+ // in current folder and subfolders.
+
+ MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains
+ // wildcards and as MATCH_EXACTPATH otherwise.
+};
+
+#define MATCH_MODEMASK 0x0000ffff
+#define MATCH_FORCECASESENSITIVE 0x80000000
+
+bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode);
+
+#endif
diff --git a/reference/unrar/model.cpp b/reference/unrar/model.cpp
new file mode 100644
index 00000000..0b2e5c34
--- /dev/null
+++ b/reference/unrar/model.cpp
@@ -0,0 +1,617 @@
+/****************************************************************************
+ * This file is part of PPMd project *
+ * Written and distributed to public domain by Dmitry Shkarin 1997, *
+ * 1999-2000 *
+ * Contents: model description and encoding/decoding routines *
+ ****************************************************************************/
+
+static const int MAX_O=64; /* maximum allowed model order */
+const uint TOP=1 << 24, BOT=1 << 15;
+
+template
+inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; }
+
+
+inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats,
+ RARPPM_STATE& FirstState)
+{
+ RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext();
+ if ( pc )
+ {
+ pc->NumStats=1;
+ pc->OneState=FirstState;
+ pc->Suffix=this;
+ pStats->Successor=pc;
+ }
+ return pc;
+}
+
+
+ModelPPM::ModelPPM()
+{
+ MinContext=NULL;
+ MaxContext=NULL;
+ MedContext=NULL;
+}
+
+
+void ModelPPM::RestartModelRare()
+{
+ int i, k, m;
+ memset(CharMask,0,sizeof(CharMask));
+ SubAlloc.InitSubAllocator();
+ InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1;
+ MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext();
+ MinContext->Suffix=NULL;
+ OrderFall=MaxOrder;
+ MinContext->U.SummFreq=(MinContext->NumStats=256)+1;
+ FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2);
+ for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++)
+ {
+ MinContext->U.Stats[i].Symbol=i;
+ MinContext->U.Stats[i].Freq=1;
+ MinContext->U.Stats[i].Successor=NULL;
+ }
+
+ static const ushort InitBinEsc[]={
+ 0x3CDD,0x1F3F,0x59BF,0x48F3,0x64A1,0x5ABC,0x6632,0x6051
+ };
+
+ for (i=0;i < 128;i++)
+ for (k=0;k < 8;k++)
+ for (m=0;m < 64;m += 8)
+ BinSumm[i][k+m]=BIN_SCALE-InitBinEsc[k]/(i+2);
+ for (i=0;i < 25;i++)
+ for (k=0;k < 16;k++)
+ SEE2Cont[i][k].init(5*i+10);
+}
+
+
+void ModelPPM::StartModelRare(int MaxOrder)
+{
+ int i, k, m ,Step;
+ EscCount=1;
+/*
+ if (MaxOrder < 2)
+ {
+ memset(CharMask,0,sizeof(CharMask));
+ OrderFall=ModelPPM::MaxOrder;
+ MinContext=MaxContext;
+ while (MinContext->Suffix != NULL)
+ {
+ MinContext=MinContext->Suffix;
+ OrderFall--;
+ }
+ FoundState=MinContext->U.Stats;
+ MinContext=MaxContext;
+ }
+ else
+*/
+ {
+ ModelPPM::MaxOrder=MaxOrder;
+ RestartModelRare();
+ NS2BSIndx[0]=2*0;
+ NS2BSIndx[1]=2*1;
+ memset(NS2BSIndx+2,2*2,9);
+ memset(NS2BSIndx+11,2*3,256-11);
+ for (i=0;i < 3;i++)
+ NS2Indx[i]=i;
+ for (m=i, k=Step=1;i < 256;i++)
+ {
+ NS2Indx[i]=m;
+ if ( !--k )
+ {
+ k = ++Step;
+ m++;
+ }
+ }
+ memset(HB2Flag,0,0x40);
+ memset(HB2Flag+0x40,0x08,0x100-0x40);
+ DummySEE2Cont.Shift=PERIOD_BITS;
+ }
+}
+
+
+void RARPPM_CONTEXT::rescale(ModelPPM *Model)
+{
+ int OldNS=NumStats, i=NumStats-1, Adder, EscFreq;
+ RARPPM_STATE* p1, * p;
+ for (p=Model->FoundState;p != U.Stats;p--)
+ _PPMD_SWAP(p[0],p[-1]);
+ U.Stats->Freq += 4;
+ U.SummFreq += 4;
+ EscFreq=U.SummFreq-p->Freq;
+ Adder=(Model->OrderFall != 0);
+ U.SummFreq = (p->Freq=(p->Freq+Adder) >> 1);
+ do
+ {
+ EscFreq -= (++p)->Freq;
+ U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1);
+ if (p[0].Freq > p[-1].Freq)
+ {
+ RARPPM_STATE tmp=*(p1=p);
+ do
+ {
+ p1[0]=p1[-1];
+ } while (--p1 != U.Stats && tmp.Freq > p1[-1].Freq);
+ *p1=tmp;
+ }
+ } while ( --i );
+ if (p->Freq == 0)
+ {
+ do
+ {
+ i++;
+ } while ((--p)->Freq == 0);
+ EscFreq += i;
+ if ((NumStats -= i) == 1)
+ {
+ RARPPM_STATE tmp=*U.Stats;
+ do
+ {
+ tmp.Freq-=(tmp.Freq >> 1);
+ EscFreq>>=1;
+ } while (EscFreq > 1);
+ Model->SubAlloc.FreeUnits(U.Stats,(OldNS+1) >> 1);
+ *(Model->FoundState=&OneState)=tmp; return;
+ }
+ }
+ U.SummFreq += (EscFreq -= (EscFreq >> 1));
+ int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1;
+ if (n0 != n1)
+ U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1);
+ Model->FoundState=U.Stats;
+}
+
+
+inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1)
+{
+#ifdef __ICL
+ static
+#endif
+ RARPPM_STATE UpState;
+ RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor;
+ RARPPM_STATE * p, * ps[MAX_O], ** pps=ps;
+ if ( !Skip )
+ {
+ *pps++ = FoundState;
+ if ( !pc->Suffix )
+ goto NO_LOOP;
+ }
+ if ( p1 )
+ {
+ p=p1;
+ pc=pc->Suffix;
+ goto LOOP_ENTRY;
+ }
+ do
+ {
+ pc=pc->Suffix;
+ if (pc->NumStats != 1)
+ {
+ if ((p=pc->U.Stats)->Symbol != FoundState->Symbol)
+ do
+ {
+ p++;
+ } while (p->Symbol != FoundState->Symbol);
+ }
+ else
+ p=&(pc->OneState);
+LOOP_ENTRY:
+ if (p->Successor != UpBranch)
+ {
+ pc=p->Successor;
+ break;
+ }
+ *pps++ = p;
+ } while ( pc->Suffix );
+NO_LOOP:
+ if (pps == ps)
+ return pc;
+ UpState.Symbol=*(byte*) UpBranch;
+ UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1);
+ if (pc->NumStats != 1)
+ {
+ if ((byte*) pc <= SubAlloc.pText)
+ return(NULL);
+ if ((p=pc->U.Stats)->Symbol != UpState.Symbol)
+ do
+ {
+ p++;
+ } while (p->Symbol != UpState.Symbol);
+ uint cf=p->Freq-1;
+ uint s0=pc->U.SummFreq-pc->NumStats-cf;
+ UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0)));
+ }
+ else
+ UpState.Freq=pc->OneState.Freq;
+ do
+ {
+ pc = pc->createChild(this,*--pps,UpState);
+ if ( !pc )
+ return NULL;
+ } while (pps != ps);
+ return pc;
+}
+
+
+inline void ModelPPM::UpdateModel()
+{
+ RARPPM_STATE fs = *FoundState, *p = NULL;
+ RARPPM_CONTEXT *pc, *Successor;
+ uint ns1, ns, cf, sf, s0;
+ if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL)
+ {
+ if (pc->NumStats != 1)
+ {
+ if ((p=pc->U.Stats)->Symbol != fs.Symbol)
+ {
+ do
+ {
+ p++;
+ } while (p->Symbol != fs.Symbol);
+ if (p[0].Freq >= p[-1].Freq)
+ {
+ _PPMD_SWAP(p[0],p[-1]);
+ p--;
+ }
+ }
+ if (p->Freq < MAX_FREQ-9)
+ {
+ p->Freq += 2;
+ pc->U.SummFreq += 2;
+ }
+ }
+ else
+ {
+ p=&(pc->OneState);
+ p->Freq += (p->Freq < 32);
+ }
+ }
+ if ( !OrderFall )
+ {
+ MinContext=MaxContext=FoundState->Successor=CreateSuccessors(TRUE,p);
+ if ( !MinContext )
+ goto RESTART_MODEL;
+ return;
+ }
+ *SubAlloc.pText++ = fs.Symbol;
+ Successor = (RARPPM_CONTEXT*) SubAlloc.pText;
+ if (SubAlloc.pText >= SubAlloc.FakeUnitsStart)
+ goto RESTART_MODEL;
+ if ( fs.Successor )
+ {
+ if ((byte*) fs.Successor <= SubAlloc.pText &&
+ (fs.Successor=CreateSuccessors(FALSE,p)) == NULL)
+ goto RESTART_MODEL;
+ if ( !--OrderFall )
+ {
+ Successor=fs.Successor;
+ SubAlloc.pText -= (MaxContext != MinContext);
+ }
+ }
+ else
+ {
+ FoundState->Successor=Successor;
+ fs.Successor=MinContext;
+ }
+ s0=MinContext->U.SummFreq-(ns=MinContext->NumStats)-(fs.Freq-1);
+ for (pc=MaxContext;pc != MinContext;pc=pc->Suffix)
+ {
+ if ((ns1=pc->NumStats) != 1)
+ {
+ if ((ns1 & 1) == 0)
+ {
+ pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1);
+ if ( !pc->U.Stats )
+ goto RESTART_MODEL;
+ }
+ pc->U.SummFreq += (2*ns1 < ns)+2*((4*ns1 <= ns) & (pc->U.SummFreq <= 8*ns1));
+ }
+ else
+ {
+ p=(RARPPM_STATE*) SubAlloc.AllocUnits(1);
+ if ( !p )
+ goto RESTART_MODEL;
+ *p=pc->OneState;
+ pc->U.Stats=p;
+ if (p->Freq < MAX_FREQ/4-1)
+ p->Freq += p->Freq;
+ else
+ p->Freq = MAX_FREQ-4;
+ pc->U.SummFreq=p->Freq+InitEsc+(ns > 3);
+ }
+ cf=2*fs.Freq*(pc->U.SummFreq+6);
+ sf=s0+pc->U.SummFreq;
+ if (cf < 6*sf)
+ {
+ cf=1+(cf > sf)+(cf >= 4*sf);
+ pc->U.SummFreq += 3;
+ }
+ else
+ {
+ cf=4+(cf >= 9*sf)+(cf >= 12*sf)+(cf >= 15*sf);
+ pc->U.SummFreq += cf;
+ }
+ p=pc->U.Stats+ns1;
+ p->Successor=Successor;
+ p->Symbol = fs.Symbol;
+ p->Freq = cf;
+ pc->NumStats=++ns1;
+ }
+ MaxContext=MinContext=fs.Successor;
+ return;
+RESTART_MODEL:
+ RestartModelRare();
+ EscCount=0;
+}
+
+
+// Tabulated escapes for exponential symbol distribution
+static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+#define GET_MEAN(SUMM,SHIFT,ROUND) ((SUMM+(1 << (SHIFT-ROUND))) >> (SHIFT))
+
+
+
+inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model)
+{
+ RARPPM_STATE& rs=OneState;
+ Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol];
+ ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+
+ Model->NS2BSIndx[Suffix->NumStats-1]+
+ Model->HiBitsFlag+2*Model->HB2Flag[rs.Symbol]+
+ ((Model->RunLength >> 26) & 0x20)];
+ if (Model->Coder.GetCurrentShiftCount(TOT_BITS) < bs)
+ {
+ Model->FoundState=&rs;
+ rs.Freq += (rs.Freq < 128);
+ Model->Coder.SubRange.LowCount=0;
+ Model->Coder.SubRange.HighCount=bs;
+ bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2));
+ Model->PrevSuccess=1;
+ Model->RunLength++;
+ }
+ else
+ {
+ Model->Coder.SubRange.LowCount=bs;
+ bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2));
+ Model->Coder.SubRange.HighCount=BIN_SCALE;
+ Model->InitEsc=ExpEscape[bs >> 10];
+ Model->NumMasked=1;
+ Model->CharMask[rs.Symbol]=Model->EscCount;
+ Model->PrevSuccess=0;
+ Model->FoundState=NULL;
+ }
+}
+
+
+inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p)
+{
+ (Model->FoundState=p)->Freq += 4;
+ U.SummFreq += 4;
+ if (p[0].Freq > p[-1].Freq)
+ {
+ _PPMD_SWAP(p[0],p[-1]);
+ Model->FoundState=--p;
+ if (p->Freq > MAX_FREQ)
+ rescale(Model);
+ }
+}
+
+
+
+
+inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model)
+{
+ Model->Coder.SubRange.scale=U.SummFreq;
+ RARPPM_STATE* p=U.Stats;
+ int i, HiCnt;
+ int count=Model->Coder.GetCurrentCount();
+ if (count>=(int)Model->Coder.SubRange.scale)
+ return(false);
+ if (count < (HiCnt=p->Freq))
+ {
+ Model->PrevSuccess=(2*(Model->Coder.SubRange.HighCount=HiCnt) > Model->Coder.SubRange.scale);
+ Model->RunLength += Model->PrevSuccess;
+ (Model->FoundState=p)->Freq=(HiCnt += 4);
+ U.SummFreq += 4;
+ if (HiCnt > MAX_FREQ)
+ rescale(Model);
+ Model->Coder.SubRange.LowCount=0;
+ return(true);
+ }
+ else
+ if (Model->FoundState==NULL)
+ return(false);
+ Model->PrevSuccess=0;
+ i=NumStats-1;
+ while ((HiCnt += (++p)->Freq) <= count)
+ if (--i == 0)
+ {
+ Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol];
+ Model->Coder.SubRange.LowCount=HiCnt;
+ Model->CharMask[p->Symbol]=Model->EscCount;
+ i=(Model->NumMasked=NumStats)-1;
+ Model->FoundState=NULL;
+ do
+ {
+ Model->CharMask[(--p)->Symbol]=Model->EscCount;
+ } while ( --i );
+ Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
+ return(true);
+ }
+ Model->Coder.SubRange.LowCount=(Model->Coder.SubRange.HighCount=HiCnt)-p->Freq;
+ update1(Model,p);
+ return(true);
+}
+
+
+inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p)
+{
+ (Model->FoundState=p)->Freq += 4;
+ U.SummFreq += 4;
+ if (p->Freq > MAX_FREQ)
+ rescale(Model);
+ Model->EscCount++;
+ Model->RunLength=Model->InitRL;
+}
+
+
+inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff)
+{
+ RARPPM_SEE2_CONTEXT* psee2c;
+ if (NumStats != 256)
+ {
+ psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+
+ (Diff < Suffix->NumStats-NumStats)+
+ 2*(U.SummFreq < 11*NumStats)+4*(Model->NumMasked > Diff)+
+ Model->HiBitsFlag;
+ Model->Coder.SubRange.scale=psee2c->getMean();
+ }
+ else
+ {
+ psee2c=&Model->DummySEE2Cont;
+ Model->Coder.SubRange.scale=1;
+ }
+ return psee2c;
+}
+
+
+
+
+inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
+{
+ int count, HiCnt, i=NumStats-Model->NumMasked;
+ RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i);
+ RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1;
+ HiCnt=0;
+ do
+ {
+ do
+ {
+ p++;
+ } while (Model->CharMask[p->Symbol] == Model->EscCount);
+ HiCnt += p->Freq;
+ *pps++ = p;
+ } while ( --i );
+ Model->Coder.SubRange.scale += HiCnt;
+ count=Model->Coder.GetCurrentCount();
+ if (count>=(int)Model->Coder.SubRange.scale)
+ return(false);
+ p=*(pps=ps);
+ if (count < HiCnt)
+ {
+ HiCnt=0;
+ while ((HiCnt += p->Freq) <= count)
+ p=*++pps;
+ Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq;
+ psee2c->update();
+ update2(Model,p);
+ }
+ else
+ {
+ Model->Coder.SubRange.LowCount=HiCnt;
+ Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
+ i=NumStats-Model->NumMasked;
+ pps--;
+ do
+ {
+ Model->CharMask[(*++pps)->Symbol]=Model->EscCount;
+ } while ( --i );
+ psee2c->Summ += Model->Coder.SubRange.scale;
+ Model->NumMasked = NumStats;
+ }
+ return(true);
+}
+
+
+inline void ModelPPM::ClearMask()
+{
+ EscCount=1;
+ memset(CharMask,0,sizeof(CharMask));
+}
+
+
+
+
+// reset PPM variables after data error allowing safe resuming
+// of further data processing
+void ModelPPM::CleanUp()
+{
+ SubAlloc.StopSubAllocator();
+ SubAlloc.StartSubAllocator(1);
+ StartModelRare(2);
+}
+
+
+bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar)
+{
+ int MaxOrder=UnpackRead->GetChar();
+ bool Reset=(MaxOrder & 0x20)!=0;
+
+ int MaxMB;
+ if (Reset)
+ MaxMB=UnpackRead->GetChar();
+ else
+ if (SubAlloc.GetAllocatedMemory()==0)
+ return(false);
+ if (MaxOrder & 0x40)
+ EscChar=UnpackRead->GetChar();
+ Coder.InitDecoder(UnpackRead);
+ if (Reset)
+ {
+ MaxOrder=(MaxOrder & 0x1f)+1;
+ if (MaxOrder>16)
+ MaxOrder=16+(MaxOrder-16)*3;
+ if (MaxOrder==1)
+ {
+ SubAlloc.StopSubAllocator();
+ return(false);
+ }
+ SubAlloc.StartSubAllocator(MaxMB+1);
+ StartModelRare(MaxOrder);
+ }
+ return(MinContext!=NULL);
+}
+
+
+int ModelPPM::DecodeChar()
+{
+ if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd)
+ return(-1);
+ if (MinContext->NumStats != 1)
+ {
+ if ((byte*)MinContext->U.Stats <= SubAlloc.pText || (byte*)MinContext->U.Stats>SubAlloc.HeapEnd)
+ return(-1);
+ if (!MinContext->decodeSymbol1(this))
+ return(-1);
+ }
+ else
+ MinContext->decodeBinSymbol(this);
+ Coder.Decode();
+ while ( !FoundState )
+ {
+ ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead);
+ do
+ {
+ OrderFall++;
+ MinContext=MinContext->Suffix;
+ if ((byte*)MinContext <= SubAlloc.pText || (byte*)MinContext>SubAlloc.HeapEnd)
+ return(-1);
+ } while (MinContext->NumStats == NumMasked);
+ if (!MinContext->decodeSymbol2(this))
+ return(-1);
+ Coder.Decode();
+ }
+ int Symbol=FoundState->Symbol;
+ if (!OrderFall && (byte*) FoundState->Successor > SubAlloc.pText)
+ MinContext=MaxContext=FoundState->Successor;
+ else
+ {
+ UpdateModel();
+ if (EscCount == 0)
+ ClearMask();
+ }
+ ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead);
+ return(Symbol);
+}
diff --git a/reference/unrar/model.hpp b/reference/unrar/model.hpp
new file mode 100644
index 00000000..52abc89b
--- /dev/null
+++ b/reference/unrar/model.hpp
@@ -0,0 +1,122 @@
+#ifndef _RAR_PPMMODEL_
+#define _RAR_PPMMODEL_
+
+#include "coder.hpp"
+#include "suballoc.hpp"
+
+#ifdef ALLOW_MISALIGNED
+#pragma pack(1)
+#endif
+
+struct RARPPM_DEF
+{
+ static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS,
+ INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124;
+};
+
+struct RARPPM_SEE2_CONTEXT : RARPPM_DEF
+{ // SEE-contexts for PPM-contexts with masked symbols
+ ushort Summ;
+ byte Shift, Count;
+ void init(int InitVal)
+ {
+ Summ=InitVal << (Shift=PERIOD_BITS-4);
+ Count=4;
+ }
+ uint getMean()
+ {
+ uint RetVal=GET_SHORT16(Summ) >> Shift;
+ Summ -= RetVal;
+ return RetVal+(RetVal == 0);
+ }
+ void update()
+ {
+ if (Shift < PERIOD_BITS && --Count == 0)
+ {
+ Summ += Summ;
+ Count=3 << Shift++;
+ }
+ }
+};
+
+
+class ModelPPM;
+struct RARPPM_CONTEXT;
+
+struct RARPPM_STATE
+{
+ byte Symbol;
+ byte Freq;
+ RARPPM_CONTEXT* Successor;
+};
+
+
+struct RARPPM_CONTEXT : RARPPM_DEF
+{
+ ushort NumStats;
+
+ struct FreqData
+ {
+ ushort SummFreq;
+ RARPPM_STATE RARPPM_PACK_ATTR * Stats;
+ };
+
+ union
+ {
+ FreqData U;
+ RARPPM_STATE OneState;
+ };
+
+ RARPPM_CONTEXT* Suffix;
+ inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder:
+ inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context
+ inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix
+ inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor
+ inline bool decodeSymbol1(ModelPPM *Model); // other orders:
+ inline bool decodeSymbol2(ModelPPM *Model); // BCD context
+ inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix
+ inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor
+ void rescale(ModelPPM *Model);
+ inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState);
+ inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff);
+};
+
+#ifdef ALLOW_MISALIGNED
+#ifdef _AIX
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+#endif
+
+class ModelPPM : RARPPM_DEF
+{
+ private:
+ friend struct RARPPM_CONTEXT;
+
+ RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont;
+
+ struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext;
+ RARPPM_STATE* FoundState; // found next state transition
+ int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL;
+ byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+ byte EscCount, PrevSuccess, HiBitsFlag;
+ ushort BinSumm[128][64]; // binary SEE-contexts
+
+ RangeCoder Coder;
+ SubAllocator SubAlloc;
+
+ void RestartModelRare();
+ void StartModelRare(int MaxOrder);
+ inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1);
+
+ inline void UpdateModel();
+ inline void ClearMask();
+ public:
+ ModelPPM();
+ void CleanUp(); // reset PPM variables after data error
+ bool DecodeInit(Unpack *UnpackRead,int &EscChar);
+ int DecodeChar();
+};
+
+#endif
diff --git a/reference/unrar/options.cpp b/reference/unrar/options.cpp
new file mode 100644
index 00000000..b53edc66
--- /dev/null
+++ b/reference/unrar/options.cpp
@@ -0,0 +1,35 @@
+#include "rar.hpp"
+
+RAROptions::RAROptions()
+{
+ Init();
+}
+
+
+RAROptions::~RAROptions()
+{
+ // It is important for security reasons, so we do not have the unnecessary
+ // password data left in memory.
+ memset(this,0,sizeof(RAROptions));
+}
+
+
+void RAROptions::Init()
+{
+ memset(this,0,sizeof(RAROptions));
+ WinSize=0x2000000;
+ Overwrite=OVERWRITE_DEFAULT;
+ Method=3;
+ MsgStream=MSG_STDOUT;
+ ConvertNames=NAMES_ORIGINALCASE;
+ xmtime=EXTTIME_HIGH3;
+ FileSizeLess=INT64NDF;
+ FileSizeMore=INT64NDF;
+ HashType=HASH_CRC32;
+#ifdef RAR_SMP
+ Threads=GetNumberOfThreads();
+#endif
+#ifdef USE_QOPEN
+ QOpenMode=QOPEN_AUTO;
+#endif
+}
diff --git a/reference/unrar/options.hpp b/reference/unrar/options.hpp
new file mode 100644
index 00000000..f4198c38
--- /dev/null
+++ b/reference/unrar/options.hpp
@@ -0,0 +1,191 @@
+#ifndef _RAR_OPTIONS_
+#define _RAR_OPTIONS_
+
+#define DEFAULT_RECOVERY -3
+
+#define DEFAULT_RECVOLUMES -10
+
+#define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size.
+
+enum PATH_EXCL_MODE {
+ EXCL_UNCHANGED=0, // Process paths as is (default).
+ EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely)
+ EXCL_BASEPATH, // -ep1 (exclude the base part of path)
+ EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter)
+ EXCL_ABSPATH, // -ep3 (the full path with the disk letter)
+
+ EXCL_SKIPABSPATH // Works as EXCL_BASEPATH for fully qualified paths
+ // and as EXCL_UNCHANGED for relative paths.
+ // Used by WinRAR GUI only.
+};
+
+enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4,
+ SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16};
+
+enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST};
+
+enum EXTTIME_MODE {
+ EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_HIGH1,EXTTIME_HIGH2,EXTTIME_HIGH3
+};
+
+enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE};
+
+enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL};
+
+enum RECURSE_MODE
+{
+ RECURSE_NONE=0, // no recurse switches
+ RECURSE_DISABLE, // switch -r-
+ RECURSE_ALWAYS, // switch -r
+ RECURSE_WILDCARDS, // switch -r0
+};
+
+enum OVERWRITE_MODE
+{
+ OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving.
+ OVERWRITE_ALL,
+ OVERWRITE_NONE,
+ OVERWRITE_AUTORENAME,
+ OVERWRITE_FORCE_ASK
+};
+
+
+enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS };
+
+enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 };
+
+#define MAX_FILTER_TYPES 16
+enum FilterState {FILTER_DEFAULT=0,FILTER_AUTO,FILTER_FORCE,FILTER_DISABLE};
+
+
+enum SAVECOPY_MODE {
+ SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT,
+ SAVECOPY_DUPLISTEXIT
+};
+
+struct FilterMode
+{
+ FilterState State;
+ int Param1;
+ int Param2;
+};
+
+#define MAX_GENERATE_MASK 128
+
+
+class RAROptions
+{
+ public:
+ RAROptions();
+ ~RAROptions();
+ void Init();
+
+ uint ExclFileAttr;
+ uint InclFileAttr;
+ bool InclAttrSet;
+ size_t WinSize;
+ wchar TempPath[NM];
+#ifdef USE_QOPEN
+ wchar SFXModule[NM];
+ QOPEN_MODE QOpenMode;
+#endif
+ bool ConfigDisabled; // Switch -cfg-.
+ wchar ExtrPath[NM];
+ wchar CommentFile[NM];
+ RAR_CHARSET CommentCharset;
+ RAR_CHARSET FilelistCharset;
+ RAR_CHARSET ErrlogCharset;
+ RAR_CHARSET RedirectCharset;
+
+ wchar ArcPath[NM];
+ SecPassword Password;
+ bool EncryptHeaders;
+
+ bool ManualPassword; // Password entered manually during operation, might need to clean for next archive.
+
+ wchar LogName[NM];
+ MESSAGE_TYPE MsgStream;
+ bool Sound;
+ OVERWRITE_MODE Overwrite;
+ int Method;
+ HASH_TYPE HashType;
+ int Recovery;
+ int RecVolNumber;
+ bool DisablePercentage;
+ bool DisableCopyright;
+ bool DisableDone;
+ bool PrintVersion;
+ int Solid;
+ int SolidCount;
+ bool ClearArc;
+ bool AddArcOnly;
+ bool DisableComment;
+ bool FreshFiles;
+ bool UpdateFiles;
+ PATH_EXCL_MODE ExclPath;
+ RECURSE_MODE Recurse;
+ int64 VolSize;
+ Array NextVolSizes;
+ uint CurVolNum;
+ bool AllYes;
+ bool DisableSortSolid;
+ int ArcTime;
+ int ConvertNames;
+ bool ProcessOwners;
+ bool SaveSymLinks;
+ bool SaveHardLinks;
+ bool AbsoluteLinks;
+ int Priority;
+ int SleepTime;
+ bool KeepBroken;
+ bool OpenShared;
+ bool DeleteFiles;
+
+#ifdef _WIN_ALL
+ bool AllowIncompatNames; // Allow names with trailing dots and spaces.
+#endif
+
+
+#ifndef SFX_MODULE
+ bool GenerateArcName;
+ wchar GenerateMask[MAX_GENERATE_MASK];
+#endif
+ bool SyncFiles;
+ bool ProcessEA;
+ bool SaveStreams;
+ bool SetCompressedAttr;
+ bool IgnoreGeneralAttr;
+ RarTime FileTimeBefore;
+ RarTime FileTimeAfter;
+ int64 FileSizeLess;
+ int64 FileSizeMore;
+ bool Lock;
+ bool Test;
+ bool VolumePause;
+ FilterMode FilterModes[MAX_FILTER_TYPES];
+ wchar EmailTo[NM];
+ uint VersionControl;
+ bool AppendArcNameToPath;
+ bool Shutdown;
+ EXTTIME_MODE xmtime;
+ EXTTIME_MODE xctime;
+ EXTTIME_MODE xatime;
+ wchar CompressStdin[NM];
+
+ uint Threads; // We use it to init hash even if RAR_SMP is not defined.
+
+
+
+
+
+#ifdef RARDLL
+ wchar DllDestName[NM];
+ int DllOpMode;
+ int DllError;
+ LPARAM UserData;
+ UNRARCALLBACK Callback;
+ CHANGEVOLPROC ChangeVolProc;
+ PROCESSDATAPROC ProcessDataProc;
+#endif
+};
+#endif
diff --git a/reference/unrar/os.hpp b/reference/unrar/os.hpp
new file mode 100644
index 00000000..a4514904
--- /dev/null
+++ b/reference/unrar/os.hpp
@@ -0,0 +1,253 @@
+#ifndef _RAR_OS_
+#define _RAR_OS_
+
+#define FALSE 0
+#define TRUE 1
+
+#ifdef __EMX__
+ #define INCL_BASE
+#endif
+
+#if defined(RARDLL) && !defined(SILENT)
+#define SILENT
+#endif
+
+#include
+
+
+#if defined(_WIN_ALL) || defined(_EMX)
+
+#define LITTLE_ENDIAN
+#define NM 2048
+
+#ifdef _WIN_ALL
+
+#define STRICT
+#define UNICODE
+#undef WINVER
+#undef _WIN32_WINNT
+#define WINVER 0x0501
+#define _WIN32_WINNT 0x0501
+
+#if !defined(ZIPSFX)
+#define RAR_SMP
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+
+#include
+#include
+#include
+#pragma comment(lib, "Shlwapi.lib")
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#endif // _WIN_ALL
+
+#include
+#include
+#include
+
+#if !defined(_EMX) && !defined(_MSC_VER)
+ #include
+#endif
+#ifdef _MSC_VER
+ #if _MSC_VER<1500
+ #define for if (0) ; else for
+ #endif
+ #include
+ #include
+
+ #define USE_SSE
+ #define SSE_ALIGNMENT 16
+#else
+ #include
+#endif // _MSC_VER
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#define SAVE_LINKS
+
+#define ENABLE_ACCESS
+
+#define DefConfigName L"rar.ini"
+#define DefLogName L"rar.log"
+
+
+#define SPATHDIVIDER L"\\"
+#define CPATHDIVIDER '\\'
+#define MASKALL L"*"
+
+#define READBINARY "rb"
+#define READTEXT "rt"
+#define UPDATEBINARY "r+b"
+#define CREATEBINARY "w+b"
+#define WRITEBINARY "wb"
+#define APPENDTEXT "at"
+
+#if defined(_WIN_ALL)
+ #ifdef _MSC_VER
+ #define _stdfunction __cdecl
+ #define _forceinline __forceinline
+ #else
+ #define _stdfunction _USERENTRY
+ #define _forceinline inline
+ #endif
+#else
+ #define _stdfunction
+ #define _forceinline inline
+#endif
+
+#endif // defined(_WIN_ALL) || defined(_EMX)
+
+#ifdef _UNIX
+
+#define NM 2048
+
+#include
+#include
+#include
+#include
+#if defined(__QNXNTO__)
+ #include
+#endif
+#if defined(RAR_SMP) && defined(__APPLE__)
+ #include
+#endif
+#ifndef SFX_MODULE
+ #include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#ifdef S_IFLNK
+#define SAVE_LINKS
+#endif
+
+#if defined(__linux) || defined(__FreeBSD__)
+#include
+#define USE_LUTIMES
+#endif
+
+#define ENABLE_ACCESS
+
+#define DefConfigName L".rarrc"
+#define DefLogName L".rarlog"
+
+
+#define SPATHDIVIDER L"/"
+#define CPATHDIVIDER '/'
+#define MASKALL L"*"
+
+#define READBINARY "r"
+#define READTEXT "r"
+#define UPDATEBINARY "r+"
+#define CREATEBINARY "w+"
+#define WRITEBINARY "w"
+#define APPENDTEXT "a"
+
+#define _stdfunction
+#define _forceinline inline
+
+#ifdef _APPLE
+ #if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN
+ #undef LITTLE_ENDIAN
+ #endif
+ #if defined(__i386__) && !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN
+ #undef BIG_ENDIAN
+ #endif
+#endif
+
+#if defined(__sparc) || defined(sparc) || defined(__hpux)
+ #ifndef BIG_ENDIAN
+ #define BIG_ENDIAN
+ #endif
+#endif
+
+#if _POSIX_C_SOURCE >= 200809L
+ #define UNIX_TIME_NS // Nanosecond time precision in Unix.
+#endif
+
+#endif // _UNIX
+
+#if 0
+ #define MSGID_INT
+ typedef int MSGID;
+#else
+ typedef const wchar* MSGID;
+#endif
+
+#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required.
+ #define SSE_ALIGNMENT 1
+#endif
+
+#define safebuf static
+
+// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN.
+#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN)
+ #define LITTLE_ENDIAN
+#endif
+#if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN)
+ #define BIG_ENDIAN
+#endif
+
+#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
+ #if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64)
+ #define LITTLE_ENDIAN
+ #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__)
+ #define LITTLE_ENDIAN
+ #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__)
+ #define BIG_ENDIAN
+ #else
+ #error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them."
+ #endif
+#endif
+
+#if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+ #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
+ #undef LITTLE_ENDIAN
+ #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
+ #undef BIG_ENDIAN
+ #else
+ #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef one of them."
+ #endif
+#endif
+
+#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__)
+// Allow not aligned integer access, increases speed in some operations.
+#define ALLOW_MISALIGNED
+#endif
+
+#endif // _RAR_OS_
diff --git a/reference/unrar/pathfn.cpp b/reference/unrar/pathfn.cpp
new file mode 100644
index 00000000..4a1f79bc
--- /dev/null
+++ b/reference/unrar/pathfn.cpp
@@ -0,0 +1,983 @@
+#include "rar.hpp"
+
+wchar* PointToName(const wchar *Path)
+{
+ for (int I=(int)wcslen(Path)-1;I>=0;I--)
+ if (IsPathDiv(Path[I]))
+ return (wchar*)&Path[I+1];
+ return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path);
+}
+
+
+wchar* PointToLastChar(const wchar *Path)
+{
+ size_t Length=wcslen(Path);
+ return (wchar*)(Length>0 ? Path+Length-1:Path);
+}
+
+
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
+{
+ const wchar *DestPtr=SrcPath;
+
+ // Prevent \..\ in any part of path string.
+ for (const wchar *s=DestPtr;*s!=0;s++)
+ if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3]))
+ DestPtr=s+4;
+
+ // Remove :\ and any sequence of . and \ in the beginning of path string.
+ while (*DestPtr!=0)
+ {
+ const wchar *s=DestPtr;
+ if (s[0]!=0 && IsDriveDiv(s[1]))
+ s+=2;
+ if (s[0]=='\\' && s[1]=='\\')
+ {
+ const wchar *Slash=wcschr(s+2,'\\');
+ if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL)
+ s=Slash+1;
+ }
+ for (const wchar *t=s;*t!=0;t++)
+ if (IsPathDiv(*t))
+ s=t+1;
+ else
+ if (*t!='.')
+ break;
+ if (s==DestPtr)
+ break;
+ DestPtr=s;
+ }
+
+ // Code above does not remove last "..", doing here.
+ if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0)
+ DestPtr+=2;
+
+ if (DestPath!=NULL)
+ {
+ // SrcPath and DestPath can point to same memory area,
+ // so we use the temporary buffer for copying.
+ wchar TmpStr[NM];
+ wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr));
+ wcscpy(DestPath,TmpStr);
+ }
+ return (wchar *)DestPtr;
+}
+
+
+void SetName(wchar *FullName,const wchar *Name,size_t MaxSize)
+{
+ wchar *NamePtr=PointToName(FullName);
+ wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName));
+}
+
+
+void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize)
+{
+ if (Name==NULL || *Name==0)
+ return;
+ wchar *Dot=GetExt(Name);
+ if (Dot!=NULL)
+ *Dot=0;
+ if (NewExt!=NULL)
+ {
+ wcsncatz(Name,L".",MaxSize);
+ wcsncatz(Name,NewExt,MaxSize);
+ }
+}
+
+
+#ifndef SFX_MODULE
+void SetSFXExt(wchar *SFXName,size_t MaxSize)
+{
+ if (SFXName==NULL || *SFXName==0)
+ return;
+
+#ifdef _UNIX
+ SetExt(SFXName,L"sfx",MaxSize);
+#endif
+
+#if defined(_WIN_ALL) || defined(_EMX)
+ SetExt(SFXName,L"exe",MaxSize);
+#endif
+}
+#endif
+
+
+// 'Ext' is an extension with the leading dot, like L".rar".
+wchar *GetExt(const wchar *Name)
+{
+ return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.');
+}
+
+
+// 'Ext' is an extension without the leading dot, like L"rar".
+bool CmpExt(const wchar *Name,const wchar *Ext)
+{
+ wchar *NameExt=GetExt(Name);
+ return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0;
+}
+
+
+bool IsWildcard(const wchar *Str)
+{
+ return Str==NULL ? false:wcspbrk(Str,L"*?")!=NULL;
+}
+
+
+bool IsPathDiv(int Ch)
+{
+#ifdef _WIN_ALL
+ return Ch=='\\' || Ch=='/';
+#else
+ return Ch==CPATHDIVIDER;
+#endif
+}
+
+
+bool IsDriveDiv(int Ch)
+{
+#ifdef _UNIX
+ return false;
+#else
+ return Ch==':';
+#endif
+}
+
+
+bool IsDriveLetter(const wchar *Path)
+{
+ wchar Letter=etoupperw(Path[0]);
+ return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]);
+}
+
+
+int GetPathDisk(const wchar *Path)
+{
+ if (IsDriveLetter(Path))
+ return etoupperw(*Path)-'A';
+ else
+ return -1;
+}
+
+
+void AddEndSlash(wchar *Path,size_t MaxLength)
+{
+ size_t Length=wcslen(Path);
+ if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4))
+ Name--;
+ *Name=0;
+}
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create)
+{
+ LPMALLOC g_pMalloc;
+ SHGetMalloc(&g_pMalloc);
+ LPITEMIDLIST ppidl;
+ *Path=0;
+ bool Success=false;
+ if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR &&
+ SHGetPathFromIDList(ppidl,Path) && *Path!=0)
+ {
+ AddEndSlash(Path,MaxSize);
+ wcsncatz(Path,L"WinRAR",MaxSize);
+ Success=FileExist(Path);
+ if (!Success && Create)
+ Success=MakeDir(Path,false,0)==MKDIR_SUCCESS;
+ }
+ g_pMalloc->Free(ppidl);
+ return Success;
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create)
+{
+ *Path=0;
+
+ HKEY hKey;
+ if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0,
+ KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS)
+ {
+ DWORD DataSize=(DWORD)MaxSize,Type;
+ RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize);
+ RegCloseKey(hKey);
+ }
+
+ if (*Path==0 || !FileExist(Path))
+ if (!GetAppDataPath(Path,MaxSize,Create))
+ {
+ GetModuleFileName(NULL,Path,(DWORD)MaxSize);
+ RemoveNameFromPath(Path);
+ }
+}
+#endif
+
+
+#ifndef SFX_MODULE
+bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create)
+{
+#ifdef _UNIX
+ static const wchar *ConfPath[]={
+ L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc"
+ };
+ if (Number==0)
+ {
+ char *EnvStr=getenv("HOME");
+ if (EnvStr!=NULL)
+ GetWideName(EnvStr,NULL,Path,MaxSize);
+ else
+ wcsncpyz(Path, ConfPath[0], MaxSize);
+ return true;
+ }
+ Number--;
+ if (Number>=ASIZE(ConfPath))
+ return false;
+ wcsncpyz(Path,ConfPath[Number], MaxSize);
+ return true;
+#elif defined(_WIN_ALL)
+ if (Number>1)
+ return false;
+ if (Number==0)
+ GetRarDataPath(Path,MaxSize,Create);
+ else
+ {
+ GetModuleFileName(NULL,Path,(DWORD)MaxSize);
+ RemoveNameFromPath(Path);
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+#endif
+
+
+#ifndef SFX_MODULE
+void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create)
+{
+ *FullName=0;
+ for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++)
+ {
+ AddEndSlash(FullName,MaxSize);
+ wcsncatz(FullName,Name,MaxSize);
+ if (!CheckExist || WildFileExist(FullName))
+ break;
+ }
+}
+#endif
+
+
+// Returns a pointer to rightmost digit of volume number.
+wchar* GetVolNumPart(const wchar *ArcName)
+{
+ // Pointing to last name character.
+ const wchar *ChPtr=ArcName+wcslen(ArcName)-1;
+
+ // Skipping the archive extension.
+ while (!IsDigit(*ChPtr) && ChPtr>ArcName)
+ ChPtr--;
+
+ // Skipping the numeric part of name.
+ const wchar *NumPtr=ChPtr;
+ while (IsDigit(*NumPtr) && NumPtr>ArcName)
+ NumPtr--;
+
+ // Searching for first numeric part in names like name.part##of##.rar.
+ // Stop search on the first dot.
+ while (NumPtr>ArcName && *NumPtr!='.')
+ {
+ if (IsDigit(*NumPtr))
+ {
+ // Validate the first numeric part only if it has a dot somewhere
+ // before it.
+ wchar *Dot=wcschr(PointToName(ArcName),'.');
+ if (Dot!=NULL && Dot|\"")==NULL;
+}
+
+
+void MakeNameUsable(char *Name,bool Extended)
+{
+#ifdef _WIN_ALL
+ // In Windows we also need to convert characters not defined in current
+ // code page. This double conversion changes them to '?', which is
+ // catched by code below.
+ size_t NameLength=strlen(Name);
+ wchar NameW[NM];
+ CharToWide(Name,NameW,ASIZE(NameW));
+ WideToChar(NameW,Name,NameLength+1);
+ Name[NameLength]=0;
+#endif
+ for (char *s=Name;*s!=0;s=charnext(s))
+ {
+ if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && (byte)*s<32)
+ *s='_';
+#ifdef _EMX
+ if (*s=='=')
+ *s='_';
+#endif
+#ifndef _UNIX
+ if (s-Name>1 && *s==':')
+ *s='_';
+ // Remove ' ' and '.' before path separator, but allow .\ and ..\.
+ if ((*s==' ' || *s=='.' && s>Name && !IsPathDiv(s[-1]) && s[-1]!='.') && IsPathDiv(s[1]))
+ *s='_';
+#endif
+ }
+}
+
+
+void MakeNameUsable(wchar *Name,bool Extended)
+{
+ for (wchar *s=Name;*s!=0;s++)
+ {
+ if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32)
+ *s='_';
+#ifndef _UNIX
+ if (s-Name>1 && *s==':')
+ *s='_';
+#if 0 // We already can create such files.
+ // Remove ' ' and '.' before path separator, but allow .\ and ..\.
+ if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name &&
+ !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2]))))
+ *s='_';
+#endif
+#endif
+ }
+}
+
+
+void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength)
+{
+ size_t Copied=0;
+ for (;Copied0)
+ *Dest=0;
+ return;
+ }
+#ifdef _WIN_ALL
+ {
+ wchar FullName[NM],*NamePtr;
+ DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr);
+ if (Code==0 || Code>ASIZE(FullName))
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Src,LongName,ASIZE(LongName)))
+ Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr);
+ }
+ if (Code!=0 && Code=MaxSize)
+ Length=0;
+ wcsncpy(Root,Path,Length);
+ Root[Length]=0;
+ }
+ }
+}
+
+
+int ParseVersionFileName(wchar *Name,bool Truncate)
+{
+ int Version=0;
+ wchar *VerText=wcsrchr(Name,';');
+ if (VerText!=NULL)
+ {
+ if (Version==0)
+ Version=atoiw(VerText+1);
+ if (Truncate)
+ *VerText=0;
+ }
+ return Version;
+}
+
+
+#if !defined(SFX_MODULE)
+// Get the name of first volume. Return the leftmost digit of volume number.
+wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering)
+{
+ if (FirstName!=VolName)
+ wcsncpyz(FirstName,VolName,MaxSize);
+ wchar *VolNumStart=FirstName;
+ if (NewNumbering)
+ {
+ wchar N='1';
+
+ // From the rightmost digit of volume number to the left.
+ for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--)
+ if (IsDigit(*ChPtr))
+ {
+ *ChPtr=N; // Set the rightmost digit to '1' and others to '0'.
+ N='0';
+ }
+ else
+ if (N=='0')
+ {
+ VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number.
+ break;
+ }
+ }
+ else
+ {
+ // Old volume numbering scheme. Just set the extension to ".rar".
+ SetExt(FirstName,L"rar",MaxSize);
+ VolNumStart=GetExt(FirstName);
+ }
+ if (!FileExist(FirstName))
+ {
+ // If the first volume, which name we just generated, is not exist,
+ // check if volume with same name and any other extension is available.
+ // It can help in case of *.exe or *.sfx first volume.
+ wchar Mask[NM];
+ wcsncpyz(Mask,FirstName,ASIZE(Mask));
+ SetExt(Mask,L"*",ASIZE(Mask));
+ FindFile Find;
+ Find.SetMask(Mask);
+ FindData FD;
+ while (Find.Next(&FD))
+ {
+ Archive Arc;
+ if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume)
+ {
+ wcsncpyz(FirstName,FD.Name,MaxSize);
+ break;
+ }
+ }
+ }
+ return VolNumStart;
+}
+#endif
+
+
+#ifndef SFX_MODULE
+static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent)
+{
+ bool Prefix=false;
+ if (*GenerateMask=='+')
+ {
+ Prefix=true; // Add the time string before the archive name.
+ GenerateMask++; // Skip '+' in the beginning of time mask.
+ }
+
+ wchar Mask[MAX_GENERATE_MASK];
+ wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask));
+
+ bool QuoteMode=false,Hours=false;
+ for (uint I=0;Mask[I]!=0;I++)
+ {
+ if (Mask[I]=='{' || Mask[I]=='}')
+ {
+ QuoteMode=(Mask[I]=='{');
+ continue;
+ }
+ if (QuoteMode)
+ continue;
+ int CurChar=toupperw(Mask[I]);
+ if (CurChar=='H')
+ Hours=true;
+
+ if (Hours && CurChar=='M')
+ {
+ // Replace minutes with 'I'. We use 'M' both for months and minutes,
+ // so we treat as minutes only those 'M' which are found after hours.
+ Mask[I]='I';
+ }
+ if (CurChar=='N')
+ {
+ uint Digits=GetDigits(ArcNumber);
+ uint NCount=0;
+ while (toupperw(Mask[I+NCount])=='N')
+ NCount++;
+
+ // Here we ensure that we have enough 'N' characters to fit all digits
+ // of archive number. We'll replace them by actual number later
+ // in this function.
+ if (NCount=4)
+ CurWeek++;
+
+ char Field[10][6];
+
+ sprintf(Field[0],"%04u",rlt.Year);
+ sprintf(Field[1],"%02u",rlt.Month);
+ sprintf(Field[2],"%02u",rlt.Day);
+ sprintf(Field[3],"%02u",rlt.Hour);
+ sprintf(Field[4],"%02u",rlt.Minute);
+ sprintf(Field[5],"%02u",rlt.Second);
+ sprintf(Field[6],"%02u",(uint)CurWeek);
+ sprintf(Field[7],"%u",(uint)WeekDay+1);
+ sprintf(Field[8],"%03u",rlt.yDay+1);
+ sprintf(Field[9],"%05u",ArcNumber);
+
+ const wchar *MaskChars=L"YMDHISWAEN";
+
+ int CField[sizeof(Field)/sizeof(Field[0])];
+ memset(CField,0,sizeof(CField));
+ QuoteMode=false;
+ for (int I=0;Mask[I]!=0;I++)
+ {
+ if (Mask[I]=='{' || Mask[I]=='}')
+ {
+ QuoteMode=(Mask[I]=='{');
+ continue;
+ }
+ if (QuoteMode)
+ continue;
+ const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I]));
+ if (ChPtr!=NULL)
+ CField[ChPtr-MaskChars]++;
+ }
+
+ wchar DateText[MAX_GENERATE_MASK];
+ *DateText=0;
+ QuoteMode=false;
+ for (size_t I=0,J=0;Mask[I]!=0 && J1)
+ {
+ // If we perform non-archiving operation, we need to use the last
+ // existing archive before the first unused name. So we generate
+ // the name for (ArcNumber-1) below.
+ wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName));
+ GenArcName(NewName,GenerateMask,ArcNumber-1,ArcNumPresent);
+ }
+ break;
+ }
+ ArcNumber++;
+ }
+ wcsncpyz(ArcName,NewName,MaxSize);
+}
+#endif
+
+
+wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize)
+{
+ if (NameW!=NULL && *NameW!=0)
+ {
+ if (DestW!=NameW)
+ wcsncpy(DestW,NameW,DestSize);
+ }
+ else
+ if (Name!=NULL)
+ CharToWide(Name,DestW,DestSize);
+ else
+ *DestW=0;
+
+ // Ensure that we return a zero terminate string for security reasons.
+ if (DestSize>0)
+ DestW[DestSize-1]=0;
+
+ return DestW;
+}
+
+
+#ifdef _WIN_ALL
+// We should return 'true' even if resulting path is shorter than MAX_PATH,
+// because we can also use this function to open files with non-standard
+// characters, even if their path length is normal.
+bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
+{
+ if (*Src==0)
+ return false;
+ const wchar *Prefix=L"\\\\?\\";
+ const size_t PrefixLength=4;
+ bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]);
+ size_t SrcLength=wcslen(Src);
+ if (IsFullPath(Src)) // Paths in d:\path\name format.
+ {
+ if (IsDriveLetter(Src))
+ {
+ if (MaxSize<=PrefixLength+SrcLength)
+ return false;
+ wcsncpy(Dest,Prefix,PrefixLength);
+ wcscpy(Dest+PrefixLength,Src);
+ return true;
+ }
+ else
+ if (Src[0]=='\\' && Src[1]=='\\')
+ {
+ if (MaxSize<=PrefixLength+SrcLength+2)
+ return false;
+ wcsncpy(Dest,Prefix,PrefixLength);
+ wcscpy(Dest+PrefixLength,L"UNC");
+ wcscpy(Dest+PrefixLength+3,Src+1);
+ return true;
+ }
+ // We may be here only if we modify IsFullPath in the future.
+ return false;
+ }
+ else
+ {
+ wchar CurDir[NM];
+ DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir);
+ if (DirCode==0 || DirCode>ASIZE(CurDir)-1)
+ return false;
+
+ if (IsPathDiv(Src[0])) // Paths in \path\name format.
+ {
+ if (MaxSize<=PrefixLength+SrcLength+2)
+ return false;
+ wcsncpy(Dest,Prefix,PrefixLength);
+ wcsncpy(Dest+PrefixLength,CurDir,2); // Copy drive letter 'd:'.
+ wcscpy(Dest+PrefixLength+2,Src);
+ return true;
+ }
+ else // Paths in path\name format.
+ {
+ AddEndSlash(CurDir,ASIZE(CurDir));
+ if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength)
+ return false;
+ wcsncpy(Dest,Prefix,PrefixLength);
+ wcscpy(Dest+PrefixLength,CurDir);
+
+ if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname.
+ Src+=2;
+
+ wcsncatz(Dest,Src,MaxSize);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
+void ConvertToPrecomposed(wchar *Name,size_t NameSize)
+{
+ wchar FileName[NM];
+ if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP.
+ FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0)
+ {
+ FileName[ASIZE(FileName)-1]=0;
+ wcsncpyz(Name,FileName,NameSize);
+ }
+}
+
+
+// Remove trailing spaces and dots in file name and in dir names in path.
+void MakeNameCompatible(wchar *Name)
+{
+ int Src=0,Dest=0;
+ while (true)
+ {
+ if (IsPathDiv(Name[Src]) || Name[Src]==0)
+ for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--)
+ {
+ if (IsPathDiv(Name[I-1])) // Permit path1/./path2 paths.
+ break;
+ Dest--;
+ }
+ Name[Dest]=Name[Src];
+ if (Name[Src]==0)
+ break;
+ Src++;
+ Dest++;
+ }
+}
+#endif
diff --git a/reference/unrar/pathfn.hpp b/reference/unrar/pathfn.hpp
new file mode 100644
index 00000000..f0826946
--- /dev/null
+++ b/reference/unrar/pathfn.hpp
@@ -0,0 +1,76 @@
+#ifndef _RAR_PATHFN_
+#define _RAR_PATHFN_
+
+wchar* PointToName(const wchar *Path);
+wchar* PointToLastChar(const wchar *Path);
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath);
+void SetName(wchar *FullName,const wchar *Name,size_t MaxSize);
+void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize);
+void SetSFXExt(wchar *SFXName,size_t MaxSize);
+wchar *GetExt(const wchar *Name);
+bool CmpExt(const wchar *Name,const wchar *Ext);
+bool IsWildcard(const wchar *Str);
+bool IsPathDiv(int Ch);
+bool IsDriveDiv(int Ch);
+bool IsDriveLetter(const wchar *Path);
+int GetPathDisk(const wchar *Path);
+void AddEndSlash(wchar *Path,size_t MaxLength);
+void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize);
+void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength);
+void RemoveNameFromPath(wchar *Path);
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create);
+void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create);
+#endif
+#ifndef SFX_MODULE
+bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create);
+void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create);
+#endif
+wchar* GetVolNumPart(const wchar *ArcName);
+void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering);
+bool IsNameUsable(const wchar *Name);
+void MakeNameUsable(char *Name,bool Extended);
+void MakeNameUsable(wchar *Name,bool Extended);
+
+void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength);
+void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength);
+void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength);
+void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength);
+
+inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength)
+{
+#ifdef _WIN_ALL
+ UnixSlashToDos(SrcName,DestName,MaxLength);
+#else
+ DosSlashToUnix(SrcName,DestName,MaxLength);
+#endif
+}
+
+inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength)
+{
+#ifdef _WIN_ALL
+ UnixSlashToDos(SrcName,DestName,MaxLength);
+#else
+ DosSlashToUnix(SrcName,DestName,MaxLength);
+#endif
+}
+
+void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize);
+bool IsFullPath(const wchar *Path);
+bool IsFullRootPath(const wchar *Path);
+void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize);
+int ParseVersionFileName(wchar *Name,bool Truncate);
+wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering);
+wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize);
+
+#ifndef SFX_MODULE
+void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving);
+#endif
+
+#ifdef _WIN_ALL
+bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize);
+void ConvertToPrecomposed(wchar *Name,size_t NameSize);
+void MakeNameCompatible(wchar *Name);
+#endif
+
+#endif
diff --git a/reference/unrar/qopen.cpp b/reference/unrar/qopen.cpp
new file mode 100644
index 00000000..789e5c03
--- /dev/null
+++ b/reference/unrar/qopen.cpp
@@ -0,0 +1,272 @@
+#include "rar.hpp"
+
+QuickOpen::QuickOpen()
+{
+ Buf=NULL;
+ Init(NULL,false);
+}
+
+
+QuickOpen::~QuickOpen()
+{
+ Close();
+ delete[] Buf;
+}
+
+
+void QuickOpen::Init(Archive *Arc,bool WriteMode)
+{
+ if (Arc!=NULL) // Unless called from constructor.
+ Close();
+
+ QuickOpen::Arc=Arc;
+ QuickOpen::WriteMode=WriteMode;
+
+ ListStart=NULL;
+ ListEnd=NULL;
+
+ if (Buf==NULL)
+ Buf=new byte[MaxBufSize];
+
+ CurBufSize=0; // Current size of buffered data in write mode.
+
+ Loaded=false;
+}
+
+
+void QuickOpen::Close()
+{
+ QuickOpenItem *Item=ListStart;
+ while (Item!=NULL)
+ {
+ QuickOpenItem *Next=Item->Next;
+ delete[] Item->Header;
+ delete Item;
+ Item=Next;
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void QuickOpen::Load(uint64 BlockPos)
+{
+ if (!Loaded) // If loading the first time, perform additional intialization.
+ {
+ SeekPos=Arc->Tell();
+ UnsyncSeekPos=false;
+
+ SaveFilePos SavePos(*Arc);
+ Arc->Seek(BlockPos,SEEK_SET);
+ if (Arc->ReadHeader()==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
+ !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
+ return;
+ QLHeaderPos=Arc->CurBlockPos;
+ RawDataStart=Arc->Tell();
+ RawDataSize=Arc->SubHead.UnpSize;
+
+ Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
+ }
+
+ if (Arc->SubHead.Encrypted)
+ {
+ RAROptions *Cmd=Arc->GetRAROptions();
+#ifndef RAR_NOCRYPT
+ if (Cmd->Password.IsSet())
+ Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
+ Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
+ Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
+ else
+#endif
+ return;
+ }
+
+ RawDataPos=0;
+ ReadBufSize=0;
+ ReadBufPos=0;
+ LastReadHeader.Reset();
+ LastReadHeaderPos=0;
+
+ ReadBuffer();
+}
+
+
+bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
+{
+ if (!Loaded)
+ return false;
+ // Find next suitable cached block.
+ while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
+ if (!ReadNext())
+ break;
+ if (!Loaded)
+ {
+ // If something wrong happened, let's set the correct file pointer
+ // and stop further quick open processing.
+ if (UnsyncSeekPos)
+ Arc->File::Seek(SeekPos,SEEK_SET);
+ return false;
+ }
+
+ if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
+ {
+ memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
+ Result=Size;
+ SeekPos+=Size;
+ UnsyncSeekPos=true;
+ }
+ else
+ {
+ if (UnsyncSeekPos)
+ {
+ Arc->File::Seek(SeekPos,SEEK_SET);
+ UnsyncSeekPos=false;
+ }
+ int ReadSize=Arc->File::Read(Data,Size);
+ if (ReadSize<0)
+ {
+ Loaded=false;
+ return false;
+ }
+ Result=ReadSize;
+ SeekPos+=ReadSize;
+ }
+
+ return true;
+}
+
+
+bool QuickOpen::Seek(int64 Offset,int Method)
+{
+ if (!Loaded)
+ return false;
+
+ // Normally we process an archive sequentially from beginning to end,
+ // so we read quick open data sequentially. But some operations like
+ // archive updating involve several passes. So if we detect that file
+ // pointer is moved back, we reload quick open data from beginning.
+ if (Method==SEEK_SET && (uint64)OffsetFile::Seek(Offset,SEEK_END);
+ SeekPos=Arc->File::Tell();
+ UnsyncSeekPos=false;
+ }
+ return true;
+}
+
+
+bool QuickOpen::Tell(int64 *Pos)
+{
+ if (!Loaded)
+ return false;
+ *Pos=SeekPos;
+ return true;
+}
+
+
+uint QuickOpen::ReadBuffer()
+{
+ SaveFilePos SavePos(*Arc);
+ Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
+ size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
+ if (Arc->SubHead.Encrypted)
+ SizeToRead &= ~CRYPT_BLOCK_MASK;
+ if (SizeToRead==0)
+ return 0;
+ int ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
+ if (ReadSize<=0)
+ return 0;
+#ifndef RAR_NOCRYPT
+ if (Arc->SubHead.Encrypted)
+ Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
+#endif
+ RawDataPos+=ReadSize;
+ ReadBufSize+=ReadSize;
+ return ReadSize;
+}
+
+
+// Fill RawRead object from buffer.
+bool QuickOpen::ReadRaw(RawRead &Raw)
+{
+ if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
+ {
+ // Ensure that we have enough data to read CRC and header size.
+ size_t DataLeft=ReadBufSize-ReadBufPos;
+ memcpy(Buf,Buf+ReadBufPos,DataLeft);
+ ReadBufPos=0;
+ ReadBufSize=DataLeft;
+ ReadBuffer();
+ }
+ const size_t FirstReadSize=7;
+ if (ReadBufPos+FirstReadSize>ReadBufSize)
+ return false;
+ Raw.Read(Buf+ReadBufPos,FirstReadSize);
+ ReadBufPos+=FirstReadSize;
+
+ uint SavedCRC=Raw.Get4();
+ uint SizeBytes=Raw.GetVSize(4);
+ uint64 BlockSize=Raw.GetV();
+ int SizeToRead=int(BlockSize);
+ SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
+ if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
+ {
+ Loaded=false; // Invalid data.
+ return false;
+ }
+
+ // If rest of block data crosses buffer boundary, read it in loop.
+ size_t DataLeft=ReadBufSize-ReadBufPos;
+ while (SizeToRead>0)
+ {
+ size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
+ Raw.Read(Buf+ReadBufPos,CurSizeToRead);
+ ReadBufPos+=CurSizeToRead;
+ SizeToRead-=int(CurSizeToRead);
+ if (SizeToRead>0) // We read the entire buffer and still need more data.
+ {
+ ReadBufPos=0;
+ ReadBufSize=0;
+ if (ReadBuffer()==0)
+ return false;
+ }
+ }
+
+ return SavedCRC==Raw.GetCRC50();
+}
+
+
+// Read next cached header.
+bool QuickOpen::ReadNext()
+{
+ RawRead Raw(NULL);
+ if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
+ return false;
+ uint Flags=(uint)Raw.GetV();
+ uint64 Offset=Raw.GetV();
+ size_t HeaderSize=(size_t)Raw.GetV();
+ LastReadHeader.Alloc(HeaderSize);
+ Raw.GetB(&LastReadHeader[0],HeaderSize);
+ // Calculate the absolute position as offset from quick open service header.
+ LastReadHeaderPos=QLHeaderPos-Offset;
+ return true;
+}
diff --git a/reference/unrar/qopen.hpp b/reference/unrar/qopen.hpp
new file mode 100644
index 00000000..e04af182
--- /dev/null
+++ b/reference/unrar/qopen.hpp
@@ -0,0 +1,61 @@
+#ifndef _RAR_QOPEN_
+#define _RAR_QOPEN_
+
+struct QuickOpenItem
+{
+ byte *Header;
+ size_t HeaderSize;
+ uint64 ArcPos;
+ QuickOpenItem *Next;
+};
+
+
+class Archive;
+class RawRead;
+
+class QuickOpen
+{
+ private:
+ void Close();
+
+
+ uint ReadBuffer();
+ bool ReadRaw(RawRead &Raw);
+ bool ReadNext();
+
+ Archive *Arc;
+ bool WriteMode;
+
+ QuickOpenItem *ListStart;
+ QuickOpenItem *ListEnd;
+
+ byte *Buf;
+ static const size_t MaxBufSize=0x10000; // Must be multiple of CRYPT_BLOCK_SIZE.
+ size_t CurBufSize;
+#ifndef RAR_NOCRYPT // For shell extension.
+ CryptData Crypt;
+#endif
+
+ bool Loaded;
+ uint64 QLHeaderPos;
+ uint64 RawDataStart;
+ uint64 RawDataSize;
+ uint64 RawDataPos;
+ size_t ReadBufSize;
+ size_t ReadBufPos;
+ Array LastReadHeader;
+ uint64 LastReadHeaderPos;
+ uint64 SeekPos;
+ bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer.
+ public:
+ QuickOpen();
+ ~QuickOpen();
+ void Init(Archive *Arc,bool WriteMode);
+ void Load(uint64 BlockPos);
+ void Unload() { Loaded=false; }
+ bool Read(void *Data,size_t Size,size_t &Result);
+ bool Seek(int64 Offset,int Method);
+ bool Tell(int64 *Pos);
+};
+
+#endif
diff --git a/reference/unrar/rar.cpp b/reference/unrar/rar.cpp
new file mode 100644
index 00000000..69175b76
--- /dev/null
+++ b/reference/unrar/rar.cpp
@@ -0,0 +1,105 @@
+#include "rar.hpp"
+
+#if !defined(RARDLL)
+int main(int argc, char *argv[])
+{
+
+#ifdef _UNIX
+ setlocale(LC_ALL,"");
+#endif
+
+ InitConsole();
+ ErrHandler.SetSignalHandlers(true);
+
+#ifdef SFX_MODULE
+ wchar ModuleName[NM];
+#ifdef _WIN_ALL
+ GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName));
+#else
+ CharToWide(argv[0],ModuleName,ASIZE(ModuleName));
+#endif
+#endif
+
+#ifdef _WIN_ALL
+ SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
+
+
+#endif
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ // Must be initialized, normal initialization can be skipped in case of
+ // exception.
+ bool ShutdownOnClose=false;
+#endif
+
+ try
+ {
+
+ CommandData *Cmd=new CommandData;
+#ifdef SFX_MODULE
+ wcscpy(Cmd->Command,L"X");
+ char *Switch=argc>1 ? argv[1]:NULL;
+ if (Switch!=NULL && Cmd->IsSwitch(Switch[0]))
+ {
+ int UpperCmd=etoupper(Switch[1]);
+ switch(UpperCmd)
+ {
+ case 'T':
+ case 'V':
+ Cmd->Command[0]=UpperCmd;
+ break;
+ case '?':
+ Cmd->OutHelp(RARX_SUCCESS);
+ break;
+ }
+ }
+ Cmd->AddArcName(ModuleName);
+ Cmd->ParseDone();
+ Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source.
+#else // !SFX_MODULE
+ Cmd->ParseCommandLine(true,argc,argv);
+ if (!Cmd->ConfigDisabled)
+ {
+ Cmd->ReadConfig();
+ Cmd->ParseEnvVar();
+ }
+ Cmd->ParseCommandLine(false,argc,argv);
+#endif
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ShutdownOnClose=Cmd->Shutdown;
+#endif
+
+ uiInit(Cmd->Sound);
+ InitConsoleOptions(Cmd->MsgStream,Cmd->RedirectCharset);
+ InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset);
+ ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL);
+
+ Cmd->OutTitle();
+ Cmd->ProcessCommand();
+ delete Cmd;
+ }
+ catch (RAR_EXIT ErrCode)
+ {
+ ErrHandler.SetErrorCode(ErrCode);
+ }
+ catch (std::bad_alloc&)
+ {
+ ErrHandler.MemoryErrorMsg();
+ ErrHandler.SetErrorCode(RARX_MEMORY);
+ }
+ catch (...)
+ {
+ ErrHandler.SetErrorCode(RARX_FATAL);
+ }
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (ShutdownOnClose && ErrHandler.IsShutdownEnabled())
+ Shutdown();
+#endif
+ ErrHandler.MainExit=true;
+ return ErrHandler.GetErrorCode();
+}
+#endif
+
+
diff --git a/reference/unrar/rar.hpp b/reference/unrar/rar.hpp
new file mode 100644
index 00000000..b4adb73d
--- /dev/null
+++ b/reference/unrar/rar.hpp
@@ -0,0 +1,94 @@
+#ifndef _RAR_RARCOMMON_
+#define _RAR_RARCOMMON_
+
+#include "raros.hpp"
+#include "rartypes.hpp"
+#include "os.hpp"
+
+#ifdef RARDLL
+#include "dll.hpp"
+#endif
+
+#include "version.hpp"
+#include "rardefs.hpp"
+#include "rarlang.hpp"
+#include "unicode.hpp"
+#include "errhnd.hpp"
+#include "secpassword.hpp"
+#include "array.hpp"
+#include "timefn.hpp"
+#include "sha1.hpp"
+#include "sha256.hpp"
+#include "blake2s.hpp"
+#include "hash.hpp"
+#include "options.hpp"
+#include "rijndael.hpp"
+#include "crypt.hpp"
+#include "headers5.hpp"
+#include "headers.hpp"
+#include "pathfn.hpp"
+#include "strfn.hpp"
+#include "strlist.hpp"
+#include "file.hpp"
+#include "crc.hpp"
+#include "ui.hpp"
+#include "filefn.hpp"
+#include "filestr.hpp"
+#include "find.hpp"
+#include "scantree.hpp"
+#include "savepos.hpp"
+#include "getbits.hpp"
+#include "rdwrfn.hpp"
+#ifdef USE_QOPEN
+#include "qopen.hpp"
+#endif
+#include "archive.hpp"
+#include "match.hpp"
+#include "cmddata.hpp"
+#include "filcreat.hpp"
+#include "consio.hpp"
+#include "system.hpp"
+#ifdef _WIN_ALL
+#include "isnt.hpp"
+#endif
+#include "log.hpp"
+#include "rawint.hpp"
+#include "rawread.hpp"
+#include "encname.hpp"
+#include "resource.hpp"
+#include "compress.hpp"
+
+#include "rarvm.hpp"
+#include "model.hpp"
+
+#include "threadpool.hpp"
+
+#include "unpack.hpp"
+
+
+
+#include "extinfo.hpp"
+#include "extract.hpp"
+
+
+
+#include "list.hpp"
+
+
+#include "rs.hpp"
+#include "rs16.hpp"
+#include "recvol.hpp"
+#include "volume.hpp"
+#include "smallfn.hpp"
+
+#include "global.hpp"
+
+#if 0
+#include "benchmark.hpp"
+#endif
+
+
+
+
+
+#endif
diff --git a/reference/unrar/rardefs.hpp b/reference/unrar/rardefs.hpp
new file mode 100644
index 00000000..90d41204
--- /dev/null
+++ b/reference/unrar/rardefs.hpp
@@ -0,0 +1,31 @@
+#ifndef _RAR_DEFS_
+#define _RAR_DEFS_
+
+#define Min(x,y) (((x)<(y)) ? (x):(y))
+#define Max(x,y) (((x)>(y)) ? (x):(y))
+
+// Universal replacement of abs function for non-int arguments.
+#define Abs(x) (((x)<0) ? -(x):(x))
+
+#define ASIZE(x) (sizeof(x)/sizeof(x[0]))
+
+// MAXPASSWORD is expected to be multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE (16)
+// for CryptProtectMemory in SecPassword.
+#define MAXPASSWORD 128
+
+#define MAXSFXSIZE 0x200000
+
+#define MAXCMTSIZE 0x40000
+
+#define DefSFXName L"default.sfx"
+#define DefSortListName L"rarfiles.lst"
+
+
+#ifndef SFX_MODULE
+#define USE_QOPEN
+#endif
+
+// Produce the value, which is equal or larger than 'v' and aligned to 'a'.
+#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) )
+
+#endif
diff --git a/reference/unrar/rarlang.hpp b/reference/unrar/rarlang.hpp
new file mode 100644
index 00000000..6151d15a
--- /dev/null
+++ b/reference/unrar/rarlang.hpp
@@ -0,0 +1,10 @@
+#ifndef _RAR_LANG_
+#define _RAR_LANG_
+
+ #ifdef USE_RC
+ #include "rarres.hpp"
+ #else
+ #include "loclang.hpp"
+ #endif
+
+#endif
diff --git a/reference/unrar/raros.hpp b/reference/unrar/raros.hpp
new file mode 100644
index 00000000..4f4f2ae7
--- /dev/null
+++ b/reference/unrar/raros.hpp
@@ -0,0 +1,36 @@
+#ifndef _RAR_RAROS_
+#define _RAR_RAROS_
+
+#ifdef __EMX__
+ #define _EMX
+#endif
+
+#ifdef __DJGPP__
+ #define _DJGPP
+ #define _EMX
+#endif
+
+#if defined(__WIN32__) || defined(_WIN32)
+ #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop.
+ #ifdef _M_X64
+ #define _WIN_64
+ #else
+ #define _WIN_32
+ #endif
+#endif
+
+#if defined(ANDROID) || defined(__ANDROID__)
+ #define _UNIX
+ #define _ANDROID
+#endif
+
+#ifdef __APPLE__
+ #define _UNIX
+ #define _APPLE
+#endif
+
+#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE)
+ #define _UNIX
+#endif
+
+#endif
diff --git a/reference/unrar/rarpch.cpp b/reference/unrar/rarpch.cpp
new file mode 100644
index 00000000..c070cf74
--- /dev/null
+++ b/reference/unrar/rarpch.cpp
@@ -0,0 +1,2 @@
+// We use rarpch.cpp to create precompiled headers for MS Visual C++.
+#include "rar.hpp"
diff --git a/reference/unrar/rartypes.hpp b/reference/unrar/rartypes.hpp
new file mode 100644
index 00000000..aac9bd57
--- /dev/null
+++ b/reference/unrar/rartypes.hpp
@@ -0,0 +1,32 @@
+#ifndef _RAR_TYPES_
+#define _RAR_TYPES_
+
+#include
+
+typedef uint8_t byte; // Unsigned 8 bits.
+typedef uint16_t ushort; // Preferably 16 bits, but can be more.
+typedef unsigned int uint; // 32 bits or more.
+typedef uint32_t uint32; // 32 bits exactly.
+typedef int32_t int32; // Signed 32 bits exactly.
+typedef uint64_t uint64; // 64 bits exactly.
+typedef int64_t int64; // Signed 64 bits exactly.
+typedef wchar_t wchar; // Unicode character
+
+// Get lowest 16 bits.
+#define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff))
+
+// Make 64 bit integer from two 32 bit.
+#define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low))
+
+// Special int64 value, large enough to never be found in real life.
+// We use it in situations, when we need to indicate that parameter
+// is not defined and probably should be calculated inside of function.
+// Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it
+// compatible with 32 bit int64.
+#define INT64NDF INT32TO64(0x7fffffff,0x7fffffff)
+
+// Maximum uint64 value.
+#define MAX_UINT64 INT32TO64(0xffffffff,0xffffffff)
+#define UINT64NDF MAX_UINT64
+
+#endif
diff --git a/reference/unrar/rarvm.cpp b/reference/unrar/rarvm.cpp
new file mode 100644
index 00000000..8d8675a3
--- /dev/null
+++ b/reference/unrar/rarvm.cpp
@@ -0,0 +1,364 @@
+#include "rar.hpp"
+
+RarVM::RarVM()
+{
+ Mem=NULL;
+}
+
+
+RarVM::~RarVM()
+{
+ delete[] Mem;
+}
+
+
+void RarVM::Init()
+{
+ if (Mem==NULL)
+ Mem=new byte[VM_MEMSIZE+4];
+}
+
+
+void RarVM::Execute(VM_PreparedProgram *Prg)
+{
+ memcpy(R,Prg->InitR,sizeof(Prg->InitR));
+ Prg->FilteredData=NULL;
+ if (Prg->Type!=VMSF_NONE)
+ {
+ bool Success=ExecuteStandardFilter(Prg->Type);
+ uint BlockSize=Prg->InitR[4] & VM_MEMMASK;
+ Prg->FilteredDataSize=BlockSize;
+ if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO)
+ Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize;
+ else
+ Prg->FilteredData=Mem;
+ }
+}
+
+
+void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg)
+{
+ // Calculate the single byte XOR checksum to check validity of VM code.
+ byte XorSum=0;
+ for (uint I=1;IType=StdList[I].Type;
+ break;
+ }
+}
+
+
+uint RarVM::ReadData(BitInput &Inp)
+{
+ uint Data=Inp.fgetbits();
+ switch(Data&0xc000)
+ {
+ case 0:
+ Inp.faddbits(6);
+ return (Data>>10)&0xf;
+ case 0x4000:
+ if ((Data&0x3c00)==0)
+ {
+ Data=0xffffff00|((Data>>2)&0xff);
+ Inp.faddbits(14);
+ }
+ else
+ {
+ Data=(Data>>6)&0xff;
+ Inp.faddbits(10);
+ }
+ return Data;
+ case 0x8000:
+ Inp.faddbits(2);
+ Data=Inp.fgetbits();
+ Inp.faddbits(16);
+ return Data;
+ default:
+ Inp.faddbits(2);
+ Data=(Inp.fgetbits()<<16);
+ Inp.faddbits(16);
+ Data|=Inp.fgetbits();
+ Inp.faddbits(16);
+ return Data;
+ }
+}
+
+
+void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize)
+{
+ if (PosVM_MEMSIZE || DataSize<4)
+ return false;
+
+ const uint FileSize=0x1000000;
+ byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8;
+ for (uint CurPos=0;CurPos=0
+ RawPut4(Addr+FileSize,Data);
+ }
+ else
+ if (((Addr-FileSize) & 0x80000000)!=0) // AddrVM_MEMSIZE || DataSize<21)
+ return false;
+
+ uint CurPos=0;
+
+ FileOffset>>=4;
+
+ while (CurPos=0)
+ {
+ static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};
+ byte CmdMask=Masks[Byte];
+ if (CmdMask!=0)
+ for (uint I=0;I<=2;I++)
+ if (CmdMask & (1<VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0)
+ return false;
+
+ // Bytes from same channels are grouped to continual data blocks,
+ // so we need to place them back to their interleaving positions.
+ for (uint CurChannel=0;CurChannelVM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2)
+ return false;
+ byte *SrcData=Mem,*DestData=SrcData+DataSize;
+ const uint Channels=3;
+ for (uint CurChannel=0;CurChannel=Width+3)
+ {
+ byte *UpperData=DestData+I-Width;
+ uint UpperByte=*UpperData;
+ uint UpperLeftByte=*(UpperData-3);
+ Predicted=PrevByte+UpperByte-UpperLeftByte;
+ int pa=abs((int)(Predicted-PrevByte));
+ int pb=abs((int)(Predicted-UpperByte));
+ int pc=abs((int)(Predicted-UpperLeftByte));
+ if (pa<=pb && pa<=pc)
+ Predicted=PrevByte;
+ else
+ if (pb<=pc)
+ Predicted=UpperByte;
+ else
+ Predicted=UpperLeftByte;
+ }
+ else
+ Predicted=PrevByte;
+ DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++));
+ }
+ }
+ for (uint I=PosR,Border=DataSize-2;IVM_MEMSIZE/2 || Channels>128 || Channels==0)
+ return false;
+ for (uint CurChannel=0;CurChannel>3) & 0xff;
+
+ uint CurByte=*(SrcData++);
+
+ Predicted-=CurByte;
+ DestData[I]=Predicted;
+ PrevDelta=(signed char)(Predicted-PrevByte);
+ PrevByte=Predicted;
+
+ int D=(signed char)CurByte;
+ // Left shift of negative value is undefined behavior in C++,
+ // so we cast it to unsigned to follow the standard.
+ D=(uint)D<<3;
+
+ Dif[0]+=abs(D);
+ Dif[1]+=abs(D-D1);
+ Dif[2]+=abs(D+D1);
+ Dif[3]+=abs(D-D2);
+ Dif[4]+=abs(D+D2);
+ Dif[5]+=abs(D-D3);
+ Dif[6]+=abs(D+D3);
+
+ if ((ByteCount & 0x1f)==0)
+ {
+ uint MinDif=Dif[0],NumMinDif=0;
+ Dif[0]=0;
+ for (uint J=1;J=-16) K1--; break;
+ case 2: if (K1 < 16) K1++; break;
+ case 3: if (K2>=-16) K2--; break;
+ case 4: if (K2 < 16) K2++; break;
+ case 5: if (K3>=-16) K3--; break;
+ case 6: if (K3 < 16) K3++; break;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ return true;
+}
+
+
+uint RarVM::FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount)
+{
+ uint InAddr=BitPos/8;
+ uint InBit=BitPos&7;
+ uint BitField=(uint)Data[InAddr++];
+ BitField|=(uint)Data[InAddr++] << 8;
+ BitField|=(uint)Data[InAddr++] << 16;
+ BitField|=(uint)Data[InAddr] << 24;
+ BitField >>= InBit;
+ return BitField & (0xffffffff>>(32-BitCount));
+}
+
+
+void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount)
+{
+ uint InAddr=BitPos/8;
+ uint InBit=BitPos&7;
+ uint AndMask=0xffffffff>>(32-BitCount);
+ AndMask=~(AndMask<>8)|0xff000000;
+ BitField>>=8;
+ }
+}
diff --git a/reference/unrar/rarvm.hpp b/reference/unrar/rarvm.hpp
new file mode 100644
index 00000000..e65c4b1a
--- /dev/null
+++ b/reference/unrar/rarvm.hpp
@@ -0,0 +1,44 @@
+#ifndef _RAR_VM_
+#define _RAR_VM_
+
+#define VM_MEMSIZE 0x40000
+#define VM_MEMMASK (VM_MEMSIZE-1)
+
+enum VM_StandardFilters {
+ VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO,
+ VMSF_DELTA
+};
+
+struct VM_PreparedProgram
+{
+ VM_PreparedProgram()
+ {
+ FilteredDataSize=0;
+ Type=VMSF_NONE;
+ }
+ VM_StandardFilters Type;
+ uint InitR[7];
+ byte *FilteredData;
+ uint FilteredDataSize;
+};
+
+class RarVM
+{
+ private:
+ bool ExecuteStandardFilter(VM_StandardFilters FilterType);
+ uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount);
+ void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount);
+
+ byte *Mem;
+ uint R[8];
+ public:
+ RarVM();
+ ~RarVM();
+ void Init();
+ void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg);
+ void Execute(VM_PreparedProgram *Prg);
+ void SetMemory(size_t Pos,byte *Data,size_t DataSize);
+ static uint ReadData(BitInput &Inp);
+};
+
+#endif
diff --git a/reference/unrar/rawint.hpp b/reference/unrar/rawint.hpp
new file mode 100644
index 00000000..30379888
--- /dev/null
+++ b/reference/unrar/rawint.hpp
@@ -0,0 +1,122 @@
+#ifndef _RAR_RAWINT_
+#define _RAR_RAWINT_
+
+#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
+#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
+#define rotl32(x,n) rotls(x,n,32)
+#define rotr32(x,n) rotrs(x,n,32)
+
+inline uint RawGet2(const void *Data)
+{
+ byte *D=(byte *)Data;
+ return D[0]+(D[1]<<8);
+}
+
+
+inline uint32 RawGet4(const void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24);
+#else
+ return *(uint32 *)Data;
+#endif
+}
+
+
+inline uint64 RawGet8(const void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ return INT32TO64(RawGet4(D+4),RawGet4(D));
+#else
+ return *(uint64 *)Data;
+#endif
+}
+
+
+inline void RawPut2(uint Field,void *Data)
+{
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+}
+
+
+inline void RawPut4(uint32 Field,void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+ D[2]=(byte)(Field>>16);
+ D[3]=(byte)(Field>>24);
+#else
+ *(uint32 *)Data=Field;
+#endif
+}
+
+
+inline void RawPut8(uint64 Field,void *Data)
+{
+#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED)
+ byte *D=(byte *)Data;
+ D[0]=(byte)(Field);
+ D[1]=(byte)(Field>>8);
+ D[2]=(byte)(Field>>16);
+ D[3]=(byte)(Field>>24);
+ D[4]=(byte)(Field>>32);
+ D[5]=(byte)(Field>>40);
+ D[6]=(byte)(Field>>48);
+ D[7]=(byte)(Field>>56);
+#else
+ *(uint64 *)Data=Field;
+#endif
+}
+
+
+#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED)
+#define USE_MEM_BYTESWAP
+#endif
+
+// Load 4 big endian bytes from memory and return uint32.
+inline uint32 RawGetBE4(const byte *m)
+{
+#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
+ return _byteswap_ulong(*(uint32 *)m);
+#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+ return __builtin_bswap32(*(uint32 *)m);
+#else
+ return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3];
+#endif
+}
+
+
+// Save integer to memory as big endian.
+inline void RawPutBE4(uint32 i,byte *mem)
+{
+#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER)
+ *(uint32*)mem = _byteswap_ulong(i);
+#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+ *(uint32*)mem = __builtin_bswap32(i);
+#else
+ mem[0]=byte(i>>24);
+ mem[1]=byte(i>>16);
+ mem[2]=byte(i>>8);
+ mem[3]=byte(i);
+#endif
+}
+
+
+inline uint32 ByteSwap32(uint32 i)
+{
+#ifdef _MSC_VER
+ return _byteswap_ulong(i);
+#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2)
+ return __builtin_bswap32(i);
+#else
+ return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF);
+#endif
+}
+
+#endif
diff --git a/reference/unrar/rawread.cpp b/reference/unrar/rawread.cpp
new file mode 100644
index 00000000..d99bac84
--- /dev/null
+++ b/reference/unrar/rawread.cpp
@@ -0,0 +1,197 @@
+#include "rar.hpp"
+
+RawRead::RawRead()
+{
+ RawRead::SrcFile=NULL;
+ Reset();
+}
+
+
+RawRead::RawRead(File *SrcFile)
+{
+ RawRead::SrcFile=SrcFile;
+ Reset();
+}
+
+
+void RawRead::Reset()
+{
+ Data.SoftReset();
+ ReadPos=0;
+ DataSize=0;
+ Crypt=NULL;
+}
+
+
+size_t RawRead::Read(size_t Size)
+{
+ size_t ReadSize=0;
+#if !defined(RAR_NOCRYPT)
+ if (Crypt!=NULL)
+ {
+ // Full size of buffer with already read data including data read
+ // for encryption block alignment.
+ size_t FullSize=Data.Size();
+
+ // Data read for alignment and not processed yet.
+ size_t DataLeft=FullSize-DataSize;
+
+ if (Size>DataLeft) // Need to read more than we already have.
+ {
+ size_t SizeToRead=Size-DataLeft;
+ size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK);
+ Data.Add(AlignedReadSize);
+ ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize);
+ Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize);
+ DataSize+=ReadSize==0 ? 0:Size;
+ }
+ else // Use buffered data, no real read.
+ {
+ ReadSize=Size;
+ DataSize+=Size;
+ }
+ }
+ else
+#endif
+ if (Size!=0)
+ {
+ Data.Add(Size);
+ ReadSize=SrcFile->Read(&Data[DataSize],Size);
+ DataSize+=ReadSize;
+ }
+ return ReadSize;
+}
+
+
+void RawRead::Read(byte *SrcData,size_t Size)
+{
+ if (Size!=0)
+ {
+ Data.Add(Size);
+ memcpy(&Data[DataSize],SrcData,Size);
+ DataSize+=Size;
+ }
+}
+
+
+byte RawRead::Get1()
+{
+ return ReadPos0)
+ memcpy(F,&Data[ReadPos],CopySize);
+ if (Size>CopySize)
+ memset(F+CopySize,0,Size-CopySize);
+ ReadPos+=CopySize;
+ return CopySize;
+}
+
+
+void RawRead::GetW(wchar *Field,size_t Size)
+{
+ if (ReadPos+2*Size-1 Data;
+ File *SrcFile;
+ size_t DataSize;
+ size_t ReadPos;
+ CryptData *Crypt;
+ public:
+ RawRead();
+ RawRead(File *SrcFile);
+ void Reset();
+ size_t Read(size_t Size);
+ void Read(byte *SrcData,size_t Size);
+ byte Get1();
+ ushort Get2();
+ uint Get4();
+ uint64 Get8();
+ uint64 GetV();
+ uint GetVSize(size_t Pos);
+ size_t GetB(void *Field,size_t Size);
+ void GetW(wchar *Field,size_t Size);
+ uint GetCRC15(bool ProcessedOnly);
+ uint GetCRC50();
+ byte* GetDataPtr() {return &Data[0];}
+ size_t Size() {return DataSize;}
+ size_t PaddedSize() {return Data.Size()-DataSize;}
+ size_t DataLeft() {return DataSize-ReadPos;}
+ size_t GetPos() {return ReadPos;}
+ void SetPos(size_t Pos) {ReadPos=Pos;}
+ void Skip(size_t Size) {ReadPos+=Size;}
+ void Rewind() {SetPos(0);}
+ void SetCrypt(CryptData *Crypt) {RawRead::Crypt=Crypt;}
+};
+
+uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow);
+
+#endif
diff --git a/reference/unrar/rdwrfn.cpp b/reference/unrar/rdwrfn.cpp
new file mode 100644
index 00000000..5d2598e4
--- /dev/null
+++ b/reference/unrar/rdwrfn.cpp
@@ -0,0 +1,319 @@
+#include "rar.hpp"
+
+ComprDataIO::ComprDataIO()
+{
+#ifndef RAR_NOCRYPT
+ Crypt=new CryptData;
+ Decrypt=new CryptData;
+#endif
+
+ Init();
+}
+
+
+void ComprDataIO::Init()
+{
+ UnpackFromMemory=false;
+ UnpackToMemory=false;
+ UnpPackedSize=0;
+ ShowProgress=true;
+ TestMode=false;
+ SkipUnpCRC=false;
+ PackVolume=false;
+ UnpVolume=false;
+ NextVolumeMissing=false;
+ SrcFile=NULL;
+ DestFile=NULL;
+ UnpWrSize=0;
+ Command=NULL;
+ Encryption=false;
+ Decryption=false;
+ CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0;
+ LastPercent=-1;
+ SubHead=NULL;
+ SubHeadPos=NULL;
+ CurrentCommand=0;
+ ProcessedArcSize=TotalArcSize=0;
+}
+
+
+ComprDataIO::~ComprDataIO()
+{
+#ifndef RAR_NOCRYPT
+ delete Crypt;
+ delete Decrypt;
+#endif
+}
+
+
+
+
+int ComprDataIO::UnpRead(byte *Addr,size_t Count)
+{
+#ifndef RAR_NOCRYPT
+ // In case of encryption we need to align read size to encryption
+ // block size. We can do it by simple masking, because unpack read code
+ // always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0.
+ if (Decryption)
+ Count &= ~CRYPT_BLOCK_MASK;
+#endif
+
+ int ReadSize=0,TotalRead=0;
+ byte *ReadAddr;
+ ReadAddr=Addr;
+ while (Count > 0)
+ {
+ Archive *SrcArc=(Archive *)SrcFile;
+
+ if (UnpackFromMemory)
+ {
+ memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize);
+ ReadSize=(int)UnpackFromMemorySize;
+ UnpackFromMemorySize=0;
+ }
+ else
+ {
+ size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count;
+ if (SizeToRead > 0)
+ {
+ if (UnpVolume && Decryption && (int64)Count>UnpPackedSize)
+ {
+ // We need aligned blocks for decryption and we want "Keep broken
+ // files" to work efficiently with missing encrypted volumes.
+ // So for last data block in volume we adjust the size to read to
+ // next equal or smaller block producing aligned total block size.
+ // So we'll ask for next volume only when processing few unaligned
+ // bytes left in the end, when most of data is already extracted.
+ size_t NewTotalRead = TotalRead + SizeToRead;
+ size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK);
+ size_t NewSizeToRead = SizeToRead - Adjust;
+ if ((int)NewSizeToRead > 0)
+ SizeToRead = NewSizeToRead;
+ }
+
+ if (!SrcFile->IsOpened())
+ return -1;
+ ReadSize=SrcFile->Read(ReadAddr,SizeToRead);
+ FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead;
+ if (hd->SplitAfter)
+ PackedDataHash.Update(ReadAddr,ReadSize);
+ }
+ }
+ CurUnpRead+=ReadSize;
+ TotalRead+=ReadSize;
+#ifndef NOVOLUME
+ // These variable are not used in NOVOLUME mode, so it is better
+ // to exclude commands below to avoid compiler warnings.
+ ReadAddr+=ReadSize;
+ Count-=ReadSize;
+#endif
+ UnpPackedSize-=ReadSize;
+
+ // Do not ask for next volume if we read something from current volume.
+ // If next volume is missing, we need to process all data from current
+ // volume before aborting. It helps to recover all possible data
+ // in "Keep broken files" mode. But if we process encrypted data,
+ // we ask for next volume also if we have non-aligned encryption block.
+ // Since we adjust data size for decryption earlier above,
+ // it does not hurt "Keep broken files" mode efficiency.
+ if (UnpVolume && UnpPackedSize == 0 &&
+ (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) )
+ {
+#ifndef NOVOLUME
+ if (!MergeArchive(*SrcArc,this,true,CurrentCommand))
+#endif
+ {
+ NextVolumeMissing=true;
+ return -1;
+ }
+ }
+ else
+ break;
+ }
+ Archive *SrcArc=(Archive *)SrcFile;
+ if (SrcArc!=NULL)
+ ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize);
+ if (ReadSize!=-1)
+ {
+ ReadSize=TotalRead;
+#ifndef RAR_NOCRYPT
+ if (Decryption)
+ Decrypt->DecryptBlock(Addr,ReadSize);
+#endif
+ }
+ Wait();
+ return ReadSize;
+}
+
+
+#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
+// Disable the run time stack check for unrar.dll, so we can manipulate
+// with ProcessDataProc call type below. Run time check would intercept
+// a wrong ESP before we restore it.
+#pragma runtime_checks( "s", off )
+#endif
+
+void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
+{
+
+#ifdef RARDLL
+ RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions();
+ if (Cmd->DllOpMode!=RAR_SKIP)
+ {
+ if (Cmd->Callback!=NULL &&
+ Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1)
+ ErrHandler.Exit(RARX_USERBREAK);
+ if (Cmd->ProcessDataProc!=NULL)
+ {
+ // Here we preserve ESP value. It is necessary for those developers,
+ // who still define ProcessDataProc callback as "C" type function,
+ // even though in year 2001 we announced in unrar.dll whatsnew.txt
+ // that it will be PASCAL type (for compatibility with Visual Basic).
+#if defined(_MSC_VER)
+#ifndef _WIN_64
+ __asm mov ebx,esp
+#endif
+#elif defined(_WIN_ALL) && defined(__BORLANDC__)
+ _EBX=_ESP;
+#endif
+ int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
+
+ // Restore ESP after ProcessDataProc with wrongly defined calling
+ // convention broken it.
+#if defined(_MSC_VER)
+#ifndef _WIN_64
+ __asm mov esp,ebx
+#endif
+#elif defined(_WIN_ALL) && defined(__BORLANDC__)
+ _ESP=_EBX;
+#endif
+ if (RetCode==0)
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+ }
+#endif // RARDLL
+
+ UnpWrAddr=Addr;
+ UnpWrSize=Count;
+ if (UnpackToMemory)
+ {
+ if (Count <= UnpackToMemorySize)
+ {
+ memcpy(UnpackToMemoryAddr,Addr,Count);
+ UnpackToMemoryAddr+=Count;
+ UnpackToMemorySize-=Count;
+ }
+ }
+ else
+ if (!TestMode)
+ DestFile->Write(Addr,Count);
+ CurUnpWrite+=Count;
+ if (!SkipUnpCRC)
+ UnpHash.Update(Addr,Count);
+ ShowUnpWrite();
+ Wait();
+}
+
+#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
+// Restore the run time stack check for unrar.dll.
+#pragma runtime_checks( "s", restore )
+#endif
+
+
+
+
+
+
+void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
+{
+ if (ShowProgress && SrcFile!=NULL)
+ {
+ if (TotalArcSize!=0)
+ {
+ // important when processing several archives or multivolume archive
+ ArcSize=TotalArcSize;
+ ArcPos+=ProcessedArcSize;
+ }
+
+ Archive *SrcArc=(Archive *)SrcFile;
+ RAROptions *Cmd=SrcArc->GetRAROptions();
+
+ int CurPercent=ToPercent(ArcPos,ArcSize);
+ if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
+ {
+ uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize);
+ LastPercent=CurPercent;
+ }
+ }
+}
+
+
+void ComprDataIO::ShowUnpWrite()
+{
+}
+
+
+
+
+
+
+
+
+
+
+void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
+{
+ if (SrcFile!=NULL)
+ ComprDataIO::SrcFile=SrcFile;
+ if (DestFile!=NULL)
+ ComprDataIO::DestFile=DestFile;
+ LastPercent=-1;
+}
+
+
+void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
+{
+ *Data=UnpWrAddr;
+ *Size=UnpWrSize;
+}
+
+
+void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
+ SecPassword *Password,const byte *Salt,const byte *InitV,
+ uint Lg2Cnt,byte *HashKey,byte *PswCheck)
+{
+#ifndef RAR_NOCRYPT
+ if (Encrypt)
+ Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
+ else
+ Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
+#endif
+}
+
+
+#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
+void ComprDataIO::SetAV15Encryption()
+{
+ Decryption=true;
+ Decrypt->SetAV15Encryption();
+}
+#endif
+
+
+#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
+void ComprDataIO::SetCmt13Encryption()
+{
+ Decryption=true;
+ Decrypt->SetCmt13Encryption();
+}
+#endif
+
+
+
+
+void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
+{
+ UnpackToMemory=true;
+ UnpackToMemoryAddr=Addr;
+ UnpackToMemorySize=Size;
+}
diff --git a/reference/unrar/rdwrfn.hpp b/reference/unrar/rdwrfn.hpp
new file mode 100644
index 00000000..b59d5624
--- /dev/null
+++ b/reference/unrar/rdwrfn.hpp
@@ -0,0 +1,97 @@
+#ifndef _RAR_DATAIO_
+#define _RAR_DATAIO_
+
+class CmdAdd;
+class Unpack;
+
+#if 0
+// We use external i/o calls for Benchmark command.
+#define COMPRDATAIO_EXTIO
+#endif
+
+class ComprDataIO
+{
+ private:
+ void ShowUnpRead(int64 ArcPos,int64 ArcSize);
+ void ShowUnpWrite();
+
+
+ bool UnpackFromMemory;
+ size_t UnpackFromMemorySize;
+ byte *UnpackFromMemoryAddr;
+
+ bool UnpackToMemory;
+ size_t UnpackToMemorySize;
+ byte *UnpackToMemoryAddr;
+
+ size_t UnpWrSize;
+ byte *UnpWrAddr;
+
+ int64 UnpPackedSize;
+
+ bool ShowProgress;
+ bool TestMode;
+ bool SkipUnpCRC;
+
+ File *SrcFile;
+ File *DestFile;
+
+ CmdAdd *Command;
+
+ FileHeader *SubHead;
+ int64 *SubHeadPos;
+
+#ifndef RAR_NOCRYPT
+ CryptData *Crypt;
+ CryptData *Decrypt;
+#endif
+
+
+ int LastPercent;
+
+ wchar CurrentCommand;
+
+ public:
+ ComprDataIO();
+ ~ComprDataIO();
+ void Init();
+ int UnpRead(byte *Addr,size_t Count);
+ void UnpWrite(byte *Addr,size_t Count);
+ void EnableShowProgress(bool Show) {ShowProgress=Show;}
+ void GetUnpackedData(byte **Data,size_t *Size);
+ void SetPackedSizeToRead(int64 Size) {UnpPackedSize=Size;}
+ void SetTestMode(bool Mode) {TestMode=Mode;}
+ void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;}
+ void SetFiles(File *SrcFile,File *DestFile);
+ void SetCommand(CmdAdd *Cmd) {Command=Cmd;}
+ void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;}
+ void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
+ void SetAV15Encryption();
+ void SetCmt13Encryption();
+ void SetUnpackToMemory(byte *Addr,uint Size);
+ void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;}
+
+
+ bool PackVolume;
+ bool UnpVolume;
+ bool NextVolumeMissing;
+ int64 UnpArcSize;
+ int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite;
+
+
+ // Size of already processed archives.
+ // Used to calculate the total operation progress.
+ int64 ProcessedArcSize;
+
+ int64 TotalArcSize;
+
+ DataHash PackedDataHash; // Packed write and unpack read hash.
+ DataHash PackHash; // Pack read hash.
+ DataHash UnpHash; // Unpack write hash.
+
+ bool Encryption;
+ bool Decryption;
+};
+
+#endif
diff --git a/reference/unrar/readme.txt b/reference/unrar/readme.txt
new file mode 100644
index 00000000..a1f820af
--- /dev/null
+++ b/reference/unrar/readme.txt
@@ -0,0 +1,50 @@
+
+ Portable UnRAR version
+
+
+ 1. General
+
+ This package includes freeware Unrar C++ source and makefile for
+ several Unix compilers.
+
+ Unrar source is subset of RAR and generated from RAR source automatically,
+ by a small program removing blocks like '#ifndef UNRAR ... #endif'.
+ Such method is not perfect and you may find some RAR related stuff
+ unnecessary in Unrar, especially in header files.
+
+ If you wish to port Unrar to a new platform, you may need to edit
+ '#define LITTLE_ENDIAN' in os.hpp and data type definitions
+ in rartypes.hpp.
+
+ if computer architecture does not allow not aligned data access,
+ you need to undefine ALLOW_NOT_ALIGNED_INT and define
+ STRICT_ALIGNMENT_REQUIRED in os.h.
+
+ UnRAR.vcproj and UnRARDll.vcproj are projects for Microsoft Visual C++.
+ UnRARDll.vcproj lets to build unrar.dll library.
+
+
+ 2. Unrar binaries
+
+ If you compiled Unrar for OS, which is not present in "Downloads"
+ and "RAR extras" on www.rarlab.com, we will appreciate if you send
+ us the compiled executable to place it to our site.
+
+
+ 3. Acknowledgements
+
+ This source includes parts of code written by other authors.
+ Please see acknow.txt file for details.
+
+
+ 4. Legal stuff
+
+ Unrar source may be used in any software to handle RAR archives
+ without limitations free of charge, but cannot be used to re-create
+ the RAR compression algorithm, which is proprietary. Distribution
+ of modified Unrar source in separate form or as a part of other
+ software is permitted, provided that it is clearly stated in
+ the documentation and source comments that the code may not be used
+ to develop a RAR (WinRAR) compatible archiver.
+
+ More detailed license text is available in license.txt.
diff --git a/reference/unrar/recvol.cpp b/reference/unrar/recvol.cpp
new file mode 100644
index 00000000..166eef4f
--- /dev/null
+++ b/reference/unrar/recvol.cpp
@@ -0,0 +1,111 @@
+#include "rar.hpp"
+
+#include "recvol3.cpp"
+#include "recvol5.cpp"
+
+
+
+bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent)
+{
+ Archive Arc(Cmd);
+ if (!Arc.Open(Name))
+ {
+ if (!Silent)
+ ErrHandler.OpenErrorMsg(Name);
+ return false;
+ }
+
+ RARFORMAT Fmt=RARFMT15;
+ if (Arc.IsArchive(true))
+ Fmt=Arc.Format;
+ else
+ {
+ byte Sign[REV5_SIGN_SIZE];
+ Arc.Seek(0,SEEK_SET);
+ if (Arc.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0)
+ Fmt=RARFMT50;
+ }
+ Arc.Close();
+
+ // We define RecVol as local variable for proper stack unwinding when
+ // handling exceptions. So it can close and delete files on Cancel.
+ if (Fmt==RARFMT15)
+ {
+ RecVolumes3 RecVol(false);
+ return RecVol.Restore(Cmd,Name,Silent);
+ }
+ else
+ {
+ RecVolumes5 RecVol(false);
+ return RecVol.Restore(Cmd,Name,Silent);
+ }
+}
+
+
+void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name)
+{
+ wchar RevName[NM];
+ *RevName=0;
+ if (Arc!=NULL)
+ {
+ // We received .rar or .exe volume as a parameter, trying to find
+ // the matching .rev file number 1.
+ bool NewNumbering=Arc->NewNumbering;
+
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+
+ wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
+ wchar RecVolMask[NM];
+ wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
+ size_t BaseNamePartLength=VolNumStart-ArcName;
+ wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
+
+ FindFile Find;
+ Find.SetMask(RecVolMask);
+ FindData RecData;
+
+ while (Find.Next(&RecData))
+ {
+ wchar *Num=GetVolNumPart(RecData.Name);
+ if (*Num!='1') // Name must have "0...01" numeric part.
+ continue;
+ bool FirstVol=true;
+ while (--Num>=RecData.Name && IsDigit(*Num))
+ if (*Num!='0')
+ {
+ FirstVol=false;
+ break;
+ }
+ if (FirstVol)
+ {
+ wcsncpyz(RevName,RecData.Name,ASIZE(RevName));
+ Name=RevName;
+ break;
+ }
+ }
+ if (*RevName==0) // First .rev file not found.
+ return;
+ }
+
+ File RevFile;
+ if (!RevFile.Open(Name))
+ {
+ ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN.
+ return;
+ }
+ mprintf(L"\n");
+ byte Sign[REV5_SIGN_SIZE];
+ bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0;
+ RevFile.Close();
+ if (Rev5)
+ {
+ RecVolumes5 RecVol(true);
+ RecVol.Test(Cmd,Name);
+ }
+ else
+ {
+ RecVolumes3 RecVol(true);
+ RecVol.Test(Cmd,Name);
+ }
+}
diff --git a/reference/unrar/recvol.hpp b/reference/unrar/recvol.hpp
new file mode 100644
index 00000000..7f2f1adb
--- /dev/null
+++ b/reference/unrar/recvol.hpp
@@ -0,0 +1,87 @@
+#ifndef _RAR_RECVOL_
+#define _RAR_RECVOL_
+
+#define REV5_SIGN "Rar!\x1aRev"
+#define REV5_SIGN_SIZE 8
+
+class RecVolumes3
+{
+ private:
+ File *SrcFile[256];
+ Array Buf;
+
+#ifdef RAR_SMP
+ ThreadPool *RSThreadPool;
+#endif
+ public:
+ RecVolumes3(bool TestOnly);
+ ~RecVolumes3();
+ void Make(RAROptions *Cmd,wchar *ArcName);
+ bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
+ void Test(RAROptions *Cmd,const wchar *Name);
+};
+
+
+struct RecVolItem
+{
+ File *f;
+ wchar Name[NM];
+ uint CRC;
+ uint64 FileSize;
+ bool New; // Newly created RAR volume.
+ bool Valid; // If existing RAR volume is valid.
+};
+
+
+class RecVolumes5;
+struct RecRSThreadData
+{
+ RecVolumes5 *RecRSPtr;
+ RSCoder16 *RS;
+ bool Encode;
+ uint DataNum;
+ const byte *Data;
+ size_t StartPos;
+ size_t Size;
+};
+
+class RecVolumes5
+{
+ private:
+ void ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode);
+ void ProcessRS(RAROptions *Cmd,uint MaxRead,bool Encode);
+ uint ReadHeader(File *RecFile,bool FirstRev);
+
+ Array RecItems;
+
+ byte *RealReadBuffer; // Real pointer returned by 'new'.
+ byte *ReadBuffer; // Pointer aligned for SSE instructions.
+
+ byte *RealBuf; // Real pointer returned by 'new'.
+ byte *Buf; // Store ECC or recovered data here, aligned for SSE.
+ size_t RecBufferSize; // Buffer area allocated for single volume.
+
+ uint DataCount; // Number of archives.
+ uint RecCount; // Number of recovery volumes.
+ uint TotalCount; // Total number of archives and recovery volumes.
+
+ bool *ValidFlags; // Volume validity flags for recovering.
+ uint MissingVolumes; // Number of missing or bad RAR volumes.
+
+#ifdef RAR_SMP
+ ThreadPool *RecThreadPool;
+#endif
+ RecRSThreadData ThreadData[MaxPoolThreads]; // Store thread parameters.
+ public: // 'public' only because called from thread functions.
+ void ProcessAreaRS(RecRSThreadData *td);
+ public:
+ RecVolumes5(bool TestOnly);
+ ~RecVolumes5();
+ bool Restore(RAROptions *Cmd,const wchar *Name,bool Silent);
+ void Test(RAROptions *Cmd,const wchar *Name);
+};
+
+bool RecVolumesRestore(RAROptions *Cmd,const wchar *Name,bool Silent);
+void RecVolumesTest(RAROptions *Cmd,Archive *Arc,const wchar *Name);
+
+#endif
diff --git a/reference/unrar/recvol3.cpp b/reference/unrar/recvol3.cpp
new file mode 100644
index 00000000..0d18f079
--- /dev/null
+++ b/reference/unrar/recvol3.cpp
@@ -0,0 +1,543 @@
+// Buffer size for all volumes involved.
+static const size_t TotalBufferSize=0x4000000;
+
+class RSEncode // Encode or decode data area, one object per one thread.
+{
+ private:
+ RSCoder RSC;
+ public:
+ void EncodeBuf();
+ void DecodeBuf();
+
+ void Init(int RecVolNumber) {RSC.Init(RecVolNumber);}
+ byte *Buf;
+ byte *OutBuf;
+ int BufStart;
+ int BufEnd;
+ int FileNumber;
+ int RecVolNumber;
+ size_t RecBufferSize;
+ int *Erasures;
+ int EraSize;
+};
+
+
+#ifdef RAR_SMP
+THREAD_PROC(RSEncodeThread)
+{
+ RSEncode *rs=(RSEncode *)Data;
+ rs->EncodeBuf();
+}
+
+THREAD_PROC(RSDecodeThread)
+{
+ RSEncode *rs=(RSEncode *)Data;
+ rs->DecodeBuf();
+}
+#endif
+
+RecVolumes3::RecVolumes3(bool TestOnly)
+{
+ memset(SrcFile,0,sizeof(SrcFile));
+ if (TestOnly)
+ {
+#ifdef RAR_SMP
+ RSThreadPool=NULL;
+#endif
+ }
+ else
+ {
+ Buf.Alloc(TotalBufferSize);
+ memset(SrcFile,0,sizeof(SrcFile));
+#ifdef RAR_SMP
+ RSThreadPool=CreateThreadPool();
+#endif
+ }
+}
+
+
+RecVolumes3::~RecVolumes3()
+{
+ for (size_t I=0;IName;Ext--)
+ if (!IsDigit(*Ext))
+ if (*Ext=='_' && IsDigit(*(Ext-1)))
+ DigitGroup++;
+ else
+ break;
+ return DigitGroup<2;
+}
+
+
+bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
+{
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+ wchar *Ext=GetExt(ArcName);
+ bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10.
+ bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0;
+ if (RevName)
+ {
+ NewStyle=IsNewStyleRev(ArcName);
+ while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
+ Ext--;
+ wcscpy(Ext,L"*.*");
+
+ FindFile Find;
+ Find.SetMask(ArcName);
+ FindData fd;
+ while (Find.Next(&fd))
+ {
+ Archive Arc(Cmd);
+ if (Arc.WOpen(fd.Name) && Arc.IsArchive(true))
+ {
+ wcsncpyz(ArcName,fd.Name,ASIZE(ArcName));
+ break;
+ }
+ }
+ }
+
+ Archive Arc(Cmd);
+ if (!Arc.WCheckOpen(ArcName))
+ return false;
+ if (!Arc.Volume)
+ {
+ uiMsg(UIERROR_NOTVOLUME,ArcName);
+ return false;
+ }
+ bool NewNumbering=Arc.NewNumbering;
+ Arc.Close();
+
+ wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering);
+ wchar RecVolMask[NM];
+ wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask));
+ size_t BaseNamePartLength=VolNumStart-ArcName;
+ wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength);
+
+ int64 RecFileSize=0;
+
+ // We cannot display "Calculating CRC..." message here, because we do not
+ // know if we'll find any recovery volumes. We'll display it after finding
+ // the first recovery volume.
+ bool CalcCRCMessageDone=false;
+
+ FindFile Find;
+ Find.SetMask(RecVolMask);
+ FindData RecData;
+ int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0;
+ wchar PrevName[NM];
+ while (Find.Next(&RecData))
+ {
+ wchar *CurName=RecData.Name;
+ int P[3];
+ if (!RevName && !NewStyle)
+ {
+ NewStyle=true;
+
+ wchar *Dot=GetExt(CurName);
+ if (Dot!=NULL)
+ {
+ int LineCount=0;
+ Dot--;
+ while (Dot>CurName && *Dot!='.')
+ {
+ if (*Dot=='_')
+ LineCount++;
+ Dot--;
+ }
+ if (LineCount==2)
+ NewStyle=false;
+ }
+ }
+ if (NewStyle)
+ {
+ if (!CalcCRCMessageDone)
+ {
+ uiMsg(UIMSG_RECVOLCALCCHECKSUM);
+ CalcCRCMessageDone=true;
+ }
+
+ uiMsg(UIMSG_STRING,CurName);
+
+ File CurFile;
+ CurFile.TOpen(CurName);
+ CurFile.Seek(0,SEEK_END);
+ int64 Length=CurFile.Tell();
+ CurFile.Seek(Length-7,SEEK_SET);
+ for (int I=0;I<3;I++)
+ P[2-I]=CurFile.GetByte()+1;
+ uint FileCRC=0;
+ for (int I=0;I<4;I++)
+ FileCRC|=CurFile.GetByte()<<(I*8);
+ uint CalcCRC;
+ CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4);
+ if (FileCRC!=CalcCRC)
+ {
+ uiMsg(UIMSG_CHECKSUM,CurName);
+ continue;
+ }
+ }
+ else
+ {
+ wchar *Dot=GetExt(CurName);
+ if (Dot==NULL)
+ continue;
+ bool WrongParam=false;
+ for (size_t I=0;I=CurName+BaseNamePartLength);
+ P[I]=atoiw(Dot+1);
+ if (P[I]==0 || P[I]>255)
+ WrongParam=true;
+ }
+ if (WrongParam)
+ continue;
+ }
+ if (P[1]+P[2]>255)
+ continue;
+ if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2])
+ {
+ uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName);
+ return false;
+ }
+ RecVolNumber=P[1];
+ FileNumber=P[2];
+ wcscpy(PrevName,CurName);
+ File *NewFile=new File;
+ NewFile->TOpen(CurName);
+ SrcFile[FileNumber+P[0]-1]=NewFile;
+ FoundRecVolumes++;
+
+ if (RecFileSize==0)
+ RecFileSize=NewFile->FileLength();
+ }
+ if (!Silent || FoundRecVolumes!=0)
+ uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
+ if (FoundRecVolumes==0)
+ return(false);
+
+ bool WriteFlags[256];
+ memset(WriteFlags,0,sizeof(WriteFlags));
+
+ wchar LastVolName[NM];
+ *LastVolName=0;
+
+ for (int CurArcNum=0;CurArcNumTOpen(ArcName);
+ ValidVolume=NewFile->IsArchive(false);
+ if (ValidVolume)
+ {
+ while (NewFile->ReadHeader()!=0)
+ {
+ if (NewFile->GetHeaderType()==HEAD_ENDARC)
+ {
+ uiMsg(UIMSG_STRING,ArcName);
+
+ if (NewFile->EndArcHead.DataCRC)
+ {
+ uint CalcCRC;
+ CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos);
+ if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC)
+ {
+ ValidVolume=false;
+ uiMsg(UIMSG_CHECKSUM,ArcName);
+ }
+ }
+ break;
+ }
+ NewFile->SeekToNext();
+ }
+ }
+ if (!ValidVolume)
+ {
+ NewFile->Close();
+ wchar NewName[NM];
+ wcscpy(NewName,ArcName);
+ wcscat(NewName,L".bad");
+
+ uiMsg(UIMSG_BADARCHIVE,ArcName);
+ uiMsg(UIMSG_RENAMING,ArcName,NewName);
+ RenameFile(ArcName,NewName);
+ }
+ NewFile->Seek(0,SEEK_SET);
+ }
+ if (!ValidVolume)
+ {
+ // It is important to return 'false' instead of aborting here,
+ // so if we are called from extraction, we will be able to continue
+ // extracting. It may happen if .rar and .rev are on read-only disks
+ // like CDs.
+ if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD))
+ {
+ // We need to display the title of operation before the error message,
+ // to make clear for user that create error is related to recovery
+ // volumes. This is why we cannot use WCreate call here. Title must be
+ // before create error, not after that.
+
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECONSTRUCTING);
+ ErrHandler.CreateErrorMsg(ArcName);
+ return false;
+ }
+
+ WriteFlags[CurArcNum]=true;
+ MissingVolumes++;
+
+ if (CurArcNum==FileNumber-1)
+ wcscpy(LastVolName,ArcName);
+
+ uiMsg(UIMSG_MISSINGVOL,ArcName);
+ uiMsg(UIEVENT_NEWARCHIVE,ArcName);
+ }
+ SrcFile[CurArcNum]=(File*)NewFile;
+ NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering);
+ }
+
+ uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
+
+ if (MissingVolumes==0)
+ {
+ uiMsg(UIERROR_RECVOLALLEXIST);
+ return false;
+ }
+
+ if (MissingVolumes>FoundRecVolumes)
+ {
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECVOLCANNOTFIX);
+ return false;
+ }
+
+ uiMsg(UIMSG_RECONSTRUCTING);
+
+ int TotalFiles=FileNumber+RecVolNumber;
+ int Erasures[256],EraSize=0;
+
+ for (int I=0;IThreads;
+ RSEncode rse[MaxPoolThreads];
+#else
+ uint ThreadNumber=1;
+ RSEncode rse[1];
+#endif
+ for (uint I=0;IRead(&Buf[I*RecBufferSize],RecBufferSize);
+ if ((size_t)ReadSize!=RecBufferSize)
+ memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize);
+ if (ReadSize>MaxRead)
+ MaxRead=ReadSize;
+ }
+ if (MaxRead==0)
+ break;
+
+ int CurPercent=ToPercent(ProcessedSize,RecFileSize);
+ if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
+ {
+ uiProcessProgress("RC",ProcessedSize,RecFileSize);
+ LastPercent=CurPercent;
+ }
+ ProcessedSize+=MaxRead;
+
+ int BlockStart=0;
+ int BlockSize=MaxRead/ThreadNumber;
+ if (BlockSize<0x100)
+ BlockSize=MaxRead;
+
+ for (uint CurThread=0;BlockStartBuf=&Buf[0];
+ curenc->BufStart=BlockStart;
+ curenc->BufEnd=BlockStart+BlockSize;
+ curenc->FileNumber=TotalFiles;
+ curenc->RecBufferSize=RecBufferSize;
+ curenc->Erasures=Erasures;
+ curenc->EraSize=EraSize;
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ RSThreadPool->AddTask(RSDecodeThread,(void*)curenc);
+ else
+ curenc->DecodeBuf();
+#else
+ curenc->DecodeBuf();
+#endif
+
+ BlockStart+=BlockSize;
+ }
+
+#ifdef RAR_SMP
+ RSThreadPool->WaitDone();
+#endif // RAR_SMP
+
+ for (int I=0;IWrite(&Buf[I*RecBufferSize],MaxRead);
+ }
+ for (int I=0;ITell();
+ CurFile->Seek(Length-7,SEEK_SET);
+ for (int J=0;J<7;J++)
+ CurFile->PutByte(0);
+ }
+ CurFile->Close();
+ SrcFile[I]=NULL;
+ }
+ if (*LastVolName!=0)
+ {
+ // Truncate the last volume to its real size.
+ Archive Arc(Cmd);
+ if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) &&
+ Arc.SearchBlock(HEAD_ENDARC))
+ {
+ Arc.Seek(Arc.NextBlockPos,SEEK_SET);
+ char Buf[8192];
+ int ReadSize=Arc.Read(Buf,sizeof(Buf));
+ int ZeroCount=0;
+ while (ZeroCountDisablePercentage)
+ mprintf(L"\b\b\b\b100%%");
+ if (!Silent && !Cmd->DisableDone)
+ mprintf(St(MDone));
+#endif
+ return true;
+}
+
+
+void RSEncode::DecodeBuf()
+{
+ for (int BufPos=BufStart;BufPosDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS);
+ if (FileCRC==CalcCRC)
+ {
+ mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
+ }
+ else
+ {
+ uiMsg(UIERROR_CHECKSUM,VolName,VolName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ }
+
+ NextVolumeName(VolName,ASIZE(VolName),false);
+ }
+}
diff --git a/reference/unrar/recvol5.cpp b/reference/unrar/recvol5.cpp
new file mode 100644
index 00000000..8272b070
--- /dev/null
+++ b/reference/unrar/recvol5.cpp
@@ -0,0 +1,517 @@
+static const uint MaxVolumes=65535;
+
+RecVolumes5::RecVolumes5(bool TestOnly)
+{
+ RealBuf=NULL;
+ RealReadBuffer=NULL;
+
+ DataCount=0;
+ RecCount=0;
+ TotalCount=0;
+ RecBufferSize=0;
+
+ for (uint I=0;IRecRSPtr->ProcessAreaRS(td);
+}
+#endif
+
+
+void RecVolumes5::ProcessRS(RAROptions *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode)
+{
+/*
+ RSCoder16 RS;
+ RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
+ uint Count=Encode ? RecCount : MissingVolumes;
+ for (uint I=0;IThreads;
+#else
+ uint ThreadNumber=1;
+#endif
+
+ const uint MinThreadBlock=0x1000;
+ ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock);
+
+ if (ThreadNumber<1)
+ ThreadNumber=1;
+ uint ThreadDataSize=MaxRead/ThreadNumber;
+ ThreadDataSize+=(ThreadDataSize&1); // Must be even for 16-bit RS coder.
+#ifdef USE_SSE
+ ThreadDataSize=ALIGN_VALUE(ThreadDataSize,SSE_ALIGNMENT); // Alignment for SSE operations.
+#endif
+ if (ThreadDataSizeRS==NULL)
+ {
+ td->RS=new RSCoder16;
+ td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags);
+ }
+ td->DataNum=DataNum;
+ td->Data=Data;
+ td->Encode=Encode;
+ td->StartPos=CurPos;
+
+ size_t EndPos=CurPos+ThreadDataSize;
+ if (EndPos>MaxRead || I==ThreadNumber-1)
+ EndPos=MaxRead;
+
+ td->Size=EndPos-CurPos;
+
+ CurPos=EndPos;
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ RecThreadPool->AddTask(RecThreadRS,(void*)td);
+ else
+ ProcessAreaRS(td);
+#else
+ ProcessAreaRS(td);
+#endif
+ }
+#ifdef RAR_SMP
+ RecThreadPool->WaitDone();
+#endif // RAR_SMP
+}
+
+
+void RecVolumes5::ProcessAreaRS(RecRSThreadData *td)
+{
+ uint Count=td->Encode ? RecCount : MissingVolumes;
+ for (uint I=0;IRS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size);
+}
+
+
+
+
+bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
+{
+ wchar ArcName[NM];
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+
+ wchar *Num=GetVolNumPart(ArcName);
+ while (Num>ArcName && IsDigit(*(Num-1)))
+ Num--;
+ wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName));
+
+ wchar FirstVolName[NM];
+ *FirstVolName=0;
+
+ int64 RecFileSize=0;
+
+ FindFile VolFind;
+ VolFind.SetMask(ArcName);
+ FindData fd;
+ uint FoundRecVolumes=0;
+ while (VolFind.Next(&fd))
+ {
+ Wait();
+
+ Archive *Vol=new Archive(Cmd);
+ int ItemPos=-1;
+ if (Vol->WOpen(fd.Name))
+ {
+ if (CmpExt(fd.Name,L"rev"))
+ {
+ uint RecNum=ReadHeader(Vol,FoundRecVolumes==0);
+ if (RecNum!=0)
+ {
+ if (FoundRecVolumes==0)
+ RecFileSize=Vol->FileLength();
+
+ ItemPos=RecNum;
+ FoundRecVolumes++;
+ }
+ }
+ else
+ if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar")))
+ {
+ if (!Vol->Volume && !Vol->BrokenHeader)
+ {
+ uiMsg(UIERROR_NOTVOLUME,ArcName);
+ return false;
+ }
+ // We work with archive as with raw data file, so we do not want
+ // to spend time to QOpen I/O redirection.
+ Vol->QOpenUnload();
+
+ Vol->Seek(0,SEEK_SET);
+
+ // RAR volume found. Get its number, store the handle in appropriate
+ // array slot, clean slots in between if we had to grow the array.
+ wchar *Num=GetVolNumPart(fd.Name);
+ uint VolNum=0;
+ for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--)
+ VolNum+=(*Num-'0')*K;
+ if (VolNum==0 || VolNum>MaxVolumes)
+ continue;
+ size_t CurSize=RecItems.Size();
+ if (VolNum>CurSize)
+ {
+ RecItems.Alloc(VolNum);
+ for (size_t I=CurSize;If=Vol;
+ Item->New=false;
+ wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name));
+ }
+ }
+
+ if (!Silent || FoundRecVolumes!=0)
+ uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
+ if (FoundRecVolumes==0)
+ return false;
+
+ uiMsg(UIMSG_RECVOLCALCCHECKSUM);
+
+ MissingVolumes=0;
+ for (uint I=0;If!=NULL)
+ {
+ uiMsg(UIMSG_STRING,Item->Name);
+
+ uint RevCRC;
+ CalcFileSum(Item->f,&RevCRC,NULL,Cmd->Threads,INT64NDF,CALCFSUM_CURPOS);
+ Item->Valid=RevCRC==Item->CRC;
+ if (!Item->Valid)
+ {
+ uiMsg(UIMSG_CHECKSUM,Item->Name);
+
+ // Close only corrupt REV volumes here. We'll close and rename corrupt
+ // RAR volumes later, if we'll know that recovery is possible.
+ if (I>=DataCount)
+ {
+ Item->f->Close();
+ Item->f=NULL;
+ FoundRecVolumes--;
+ }
+ }
+ }
+ if (If==NULL || !Item->Valid))
+ MissingVolumes++;
+ }
+
+ uiMsg(UIMSG_RECVOLMISSING,MissingVolumes);
+
+ if (MissingVolumes==0)
+ {
+ uiMsg(UIERROR_RECVOLALLEXIST);
+ return false;
+ }
+
+ if (MissingVolumes>FoundRecVolumes)
+ {
+ uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode.
+ uiMsg(UIERROR_RECVOLCANNOTFIX);
+ return false;
+ }
+
+ uiMsg(UIMSG_RECONSTRUCTING);
+
+ // Create missing and rename bad volumes.
+ uint64 MaxVolSize=0;
+ for (uint I=0;IFileSize>MaxVolSize)
+ MaxVolSize=Item->FileSize;
+ if (Item->f!=NULL && !Item->Valid)
+ {
+ Item->f->Close();
+
+ wchar NewName[NM];
+ wcscpy(NewName,Item->Name);
+ wcscat(NewName,L".bad");
+
+ uiMsg(UIMSG_BADARCHIVE,Item->Name);
+ uiMsg(UIMSG_RENAMING,Item->Name,NewName);
+ RenameFile(Item->Name,NewName);
+ delete Item->f;
+ Item->f=NULL;
+ }
+
+ if ((Item->New=(Item->f==NULL))) // Additional parentheses to avoid GCC warning.
+ {
+ wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name));
+ uiMsg(UIMSG_CREATING,Item->Name);
+ uiMsg(UIEVENT_NEWARCHIVE,Item->Name);
+ File *NewVol=new File;
+ bool UserReject;
+ if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject))
+ {
+ if (!UserReject)
+ ErrHandler.CreateErrorMsg(Item->Name);
+ ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE);
+ }
+ NewVol->Prealloc(Item->FileSize);
+ Item->f=NewVol;
+ Item->New=true;
+ }
+ NextVolumeName(FirstVolName,ASIZE(FirstVolName),false);
+ }
+
+
+ int64 ProcessedSize=0;
+ int LastPercent=-1;
+ mprintf(L" ");
+
+ // Even though we already preliminary calculated missing volume number,
+ // let's do it again now, when we have the final and exact information.
+ MissingVolumes=0;
+
+ ValidFlags=new bool[TotalCount];
+ for (uint I=0;If!=NULL && !Item->New)
+ ReadSize=Item->f->Read(B,RecBufferSize);
+ if (ReadSize!=RecBufferSize)
+ memset(B+ReadSize,0,RecBufferSize-ReadSize);
+ if (ReadSize>MaxRead)
+ MaxRead=ReadSize;
+
+ // We can have volumes of different size. Let's use data chunk
+ // for largest volume size.
+ uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize);
+ ProcessRS(Cmd,I,B,DataToProcess,false);
+ }
+ if (MaxRead==0)
+ break;
+
+ for (uint I=0,J=0;IFileSize);
+ Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize);
+ Item->FileSize-=WriteSize;
+ }
+
+ int CurPercent=ToPercent(ProcessedSize,RecFileSize);
+ if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
+ {
+ uiProcessProgress("RV",ProcessedSize,RecFileSize);
+ LastPercent=CurPercent;
+ }
+ ProcessedSize+=MaxRead;
+ }
+
+ for (uint I=0;IClose();
+
+ delete[] ValidFlags;
+ delete[] Data;
+#if !defined(SILENT)
+ if (!Cmd->DisablePercentage)
+ mprintf(L"\b\b\b\b100%%");
+ if (!Silent && !Cmd->DisableDone)
+ mprintf(St(MDone));
+#endif
+ return true;
+}
+
+
+uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev)
+{
+ const size_t FirstReadSize=REV5_SIGN_SIZE+8;
+ byte ShortBuf[FirstReadSize];
+ if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize)
+ return 0;
+ if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0)
+ return 0;
+ uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4);
+ if (HeaderSize>0x100000 || HeaderSize<=5)
+ return 0;
+ uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE);
+
+ RawRead Raw(RecFile);
+ if (Raw.Read(HeaderSize)!=HeaderSize)
+ return 0;
+
+ // Calculate CRC32 of entire header including 4 byte size field.
+ uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4);
+ if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC)
+ return 0;
+
+ if (Raw.Get1()!=1) // Version check.
+ return 0;
+ DataCount=Raw.Get2();
+ RecCount=Raw.Get2();
+ TotalCount=DataCount+RecCount;
+ uint RecNum=Raw.Get2(); // Number of recovery volume.
+ if (RecNum>=TotalCount || TotalCount>MaxVolumes)
+ return 0;
+ uint RevCRC=Raw.Get4(); // CRC of current REV volume.
+
+ if (FirstRev)
+ {
+ // If we have read the first valid REV file, init data structures
+ // using information from REV header.
+ size_t CurSize=RecItems.Size();
+ RecItems.Alloc(TotalCount);
+ for (size_t I=CurSize;IDisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS));
+ Valid=RevCRC==RecItems[RecNum].CRC;
+ }
+
+ if (Valid)
+ {
+ mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk));
+ }
+ else
+ {
+ uiMsg(UIERROR_CHECKSUM,VolName,VolName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ }
+
+ NextVolumeName(VolName,ASIZE(VolName),false);
+ }
+}
diff --git a/reference/unrar/resource.cpp b/reference/unrar/resource.cpp
new file mode 100644
index 00000000..20a85757
--- /dev/null
+++ b/reference/unrar/resource.cpp
@@ -0,0 +1,11 @@
+#include "rar.hpp"
+
+
+
+#ifndef RARDLL
+const wchar *St(MSGID StringId)
+{
+ return StringId;
+}
+#endif
+
diff --git a/reference/unrar/resource.hpp b/reference/unrar/resource.hpp
new file mode 100644
index 00000000..98a6c6ba
--- /dev/null
+++ b/reference/unrar/resource.hpp
@@ -0,0 +1,11 @@
+#ifndef _RAR_RESOURCE_
+#define _RAR_RESOURCE_
+
+#ifdef RARDLL
+#define St(x) (L"")
+#else
+const wchar *St(MSGID StringId);
+#endif
+
+
+#endif
diff --git a/reference/unrar/rijndael.cpp b/reference/unrar/rijndael.cpp
new file mode 100644
index 00000000..a091423f
--- /dev/null
+++ b/reference/unrar/rijndael.cpp
@@ -0,0 +1,506 @@
+/***************************************************************************
+ * This code is based on public domain Szymon Stefanek AES implementation: *
+ * http://www.pragmaware.net/software/rijndael/index.php *
+ * *
+ * Dynamic tables generation is based on the Brian Gladman work: *
+ * http://fp.gladman.plus.com/cryptography_technology/rijndael *
+ ***************************************************************************/
+#include "rar.hpp"
+
+#ifdef USE_SSE
+#include
+#endif
+
+static byte S[256],S5[256],rcon[30];
+static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4];
+static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4];
+static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4];
+
+
+inline void Xor128(void *dest,const void *arg1,const void *arg2)
+{
+#ifdef ALLOW_MISALIGNED
+ ((uint32*)dest)[0]=((uint32*)arg1)[0]^((uint32*)arg2)[0];
+ ((uint32*)dest)[1]=((uint32*)arg1)[1]^((uint32*)arg2)[1];
+ ((uint32*)dest)[2]=((uint32*)arg1)[2]^((uint32*)arg2)[2];
+ ((uint32*)dest)[3]=((uint32*)arg1)[3]^((uint32*)arg2)[3];
+#else
+ for (int I=0;I<16;I++)
+ ((byte*)dest)[I]=((byte*)arg1)[I]^((byte*)arg2)[I];
+#endif
+}
+
+
+inline void Xor128(byte *dest,const byte *arg1,const byte *arg2,
+ const byte *arg3,const byte *arg4)
+{
+#ifdef ALLOW_MISALIGNED
+ (*(uint32*)dest)=(*(uint32*)arg1)^(*(uint32*)arg2)^(*(uint32*)arg3)^(*(uint32*)arg4);
+#else
+ for (int I=0;I<4;I++)
+ dest[I]=arg1[I]^arg2[I]^arg3[I]^arg4[I];
+#endif
+}
+
+
+inline void Copy128(byte *dest,const byte *src)
+{
+#ifdef ALLOW_MISALIGNED
+ ((uint32*)dest)[0]=((uint32*)src)[0];
+ ((uint32*)dest)[1]=((uint32*)src)[1];
+ ((uint32*)dest)[2]=((uint32*)src)[2];
+ ((uint32*)dest)[3]=((uint32*)src)[3];
+#else
+ for (int I=0;I<16;I++)
+ dest[I]=src[I];
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// API
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Rijndael::Rijndael()
+{
+ if (S[0]==0)
+ GenerateTables();
+ CBCMode = true; // Always true for RAR.
+}
+
+
+void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector)
+{
+#ifdef USE_SSE
+ // Check SSE here instead of constructor, so if object is a part of some
+ // structure memset'ed before use, this variable is not lost.
+ int CPUInfo[4];
+ __cpuid(CPUInfo, 1);
+ AES_NI=(CPUInfo[2] & 0x2000000)!=0;
+#endif
+
+ uint uKeyLenInBytes;
+ switch(keyLen)
+ {
+ case 128:
+ uKeyLenInBytes = 16;
+ m_uRounds = 10;
+ break;
+ case 192:
+ uKeyLenInBytes = 24;
+ m_uRounds = 12;
+ break;
+ case 256:
+ uKeyLenInBytes = 32;
+ m_uRounds = 14;
+ break;
+ }
+
+ byte keyMatrix[_MAX_KEY_COLUMNS][4];
+
+ for(uint i = 0; i < uKeyLenInBytes; i++)
+ keyMatrix[i >> 2][i & 3] = key[i];
+
+ if (initVector==NULL)
+ memset(m_initVector, 0, sizeof(m_initVector));
+ else
+ for(int i = 0; i < MAX_IV_SIZE; i++)
+ m_initVector[i] = initVector[i];
+
+ keySched(keyMatrix);
+
+ if(!Encrypt)
+ keyEncToDec();
+}
+
+void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer)
+{
+ if (inputLen <= 0)
+ return;
+
+ size_t numBlocks = inputLen/16;
+#ifdef USE_SSE
+ if (AES_NI)
+ {
+ blockEncryptSSE(input,numBlocks,outBuffer);
+ return;
+ }
+#endif
+
+ byte *prevBlock = m_initVector;
+ for(size_t i = numBlocks;i > 0;i--)
+ {
+ byte block[16];
+ if (CBCMode)
+ Xor128(block,prevBlock,input);
+ else
+ Copy128(block,input);
+
+ byte temp[4][4];
+
+ Xor128(temp,block,m_expandedKey[0]);
+ Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]);
+ Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]);
+ Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]);
+ Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]);
+
+ for(int r = 1; r < m_uRounds-1; r++)
+ {
+ Xor128(temp,outBuffer,m_expandedKey[r]);
+ Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]);
+ Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]);
+ Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]);
+ Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]);
+ }
+ Xor128(temp,outBuffer,m_expandedKey[m_uRounds-1]);
+ outBuffer[ 0] = T1[temp[0][0]][1];
+ outBuffer[ 1] = T1[temp[1][1]][1];
+ outBuffer[ 2] = T1[temp[2][2]][1];
+ outBuffer[ 3] = T1[temp[3][3]][1];
+ outBuffer[ 4] = T1[temp[1][0]][1];
+ outBuffer[ 5] = T1[temp[2][1]][1];
+ outBuffer[ 6] = T1[temp[3][2]][1];
+ outBuffer[ 7] = T1[temp[0][3]][1];
+ outBuffer[ 8] = T1[temp[2][0]][1];
+ outBuffer[ 9] = T1[temp[3][1]][1];
+ outBuffer[10] = T1[temp[0][2]][1];
+ outBuffer[11] = T1[temp[1][3]][1];
+ outBuffer[12] = T1[temp[3][0]][1];
+ outBuffer[13] = T1[temp[0][1]][1];
+ outBuffer[14] = T1[temp[1][2]][1];
+ outBuffer[15] = T1[temp[2][3]][1];
+ Xor128(outBuffer,outBuffer,m_expandedKey[m_uRounds]);
+ prevBlock=outBuffer;
+
+ outBuffer += 16;
+ input += 16;
+ }
+ Copy128(m_initVector,prevBlock);
+}
+
+
+#ifdef USE_SSE
+void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer)
+{
+ __m128i v = _mm_loadu_si128((__m128i*)m_initVector);
+ __m128i *src=(__m128i*)input;
+ __m128i *dest=(__m128i*)outBuffer;
+ __m128i *rkey=(__m128i*)m_expandedKey;
+ while (numBlocks > 0)
+ {
+ __m128i d = _mm_loadu_si128(src++);
+ if (CBCMode)
+ v = _mm_xor_si128(v, d);
+ else
+ v = d;
+ __m128i r0 = _mm_loadu_si128(rkey);
+ v = _mm_xor_si128(v, r0);
+
+ for (int i=1; i 0; i--)
+ {
+ byte temp[4][4];
+
+ Xor128(temp,input,m_expandedKey[m_uRounds]);
+
+ Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
+ Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
+ Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
+ Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
+
+ for(int r = m_uRounds-1; r > 1; r--)
+ {
+ Xor128(temp,block,m_expandedKey[r]);
+ Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]);
+ Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]);
+ Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]);
+ Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]);
+ }
+
+ Xor128(temp,block,m_expandedKey[1]);
+ block[ 0] = S5[temp[0][0]];
+ block[ 1] = S5[temp[3][1]];
+ block[ 2] = S5[temp[2][2]];
+ block[ 3] = S5[temp[1][3]];
+ block[ 4] = S5[temp[1][0]];
+ block[ 5] = S5[temp[0][1]];
+ block[ 6] = S5[temp[3][2]];
+ block[ 7] = S5[temp[2][3]];
+ block[ 8] = S5[temp[2][0]];
+ block[ 9] = S5[temp[1][1]];
+ block[10] = S5[temp[0][2]];
+ block[11] = S5[temp[3][3]];
+ block[12] = S5[temp[3][0]];
+ block[13] = S5[temp[2][1]];
+ block[14] = S5[temp[1][2]];
+ block[15] = S5[temp[0][3]];
+ Xor128(block,block,m_expandedKey[0]);
+
+ if (CBCMode)
+ Xor128(block,block,iv);
+
+ Copy128((byte*)iv,input);
+ Copy128(outBuffer,block);
+
+ input += 16;
+ outBuffer += 16;
+ }
+
+ memcpy(m_initVector,iv,16);
+}
+
+
+#ifdef USE_SSE
+void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer)
+{
+ __m128i initVector = _mm_loadu_si128((__m128i*)m_initVector);
+ __m128i *src=(__m128i*)input;
+ __m128i *dest=(__m128i*)outBuffer;
+ __m128i *rkey=(__m128i*)m_expandedKey;
+ while (numBlocks > 0)
+ {
+ __m128i rl = _mm_loadu_si128(rkey + m_uRounds);
+ __m128i d = _mm_loadu_si128(src++);
+ __m128i v = _mm_xor_si128(rl, d);
+
+ for (int i=m_uRounds-1; i>0; i--)
+ {
+ __m128i ri = _mm_loadu_si128(rkey + i);
+ v = _mm_aesdec_si128(v, ri);
+ }
+
+ __m128i r0 = _mm_loadu_si128(rkey);
+ v = _mm_aesdeclast_si128(v, r0);
+
+ if (CBCMode)
+ v = _mm_xor_si128(v, initVector);
+ initVector = d;
+ _mm_storeu_si128(dest++,v);
+ numBlocks--;
+ }
+ _mm_storeu_si128((__m128i*)m_initVector,initVector);
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ALGORITHM
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+void Rijndael::keySched(byte key[_MAX_KEY_COLUMNS][4])
+{
+ int j,rconpointer = 0;
+
+ // Calculate the necessary round keys
+ // The number of calculations depends on keyBits and blockBits
+ int uKeyColumns = m_uRounds - 6;
+
+ byte tempKey[_MAX_KEY_COLUMNS][4];
+
+ // Copy the input key to the temporary key matrix
+
+ memcpy(tempKey,key,sizeof(tempKey));
+
+ int r = 0;
+ int t = 0;
+
+ // copy values into round key array
+ for(j = 0;(j < uKeyColumns) && (r <= m_uRounds); )
+ {
+ for(;(j < uKeyColumns) && (t < 4); j++, t++)
+ for (int k=0;k<4;k++)
+ m_expandedKey[r][t][k]=tempKey[j][k];
+
+ if(t == 4)
+ {
+ r++;
+ t = 0;
+ }
+ }
+
+ while(r <= m_uRounds)
+ {
+ tempKey[0][0] ^= S[tempKey[uKeyColumns-1][1]];
+ tempKey[0][1] ^= S[tempKey[uKeyColumns-1][2]];
+ tempKey[0][2] ^= S[tempKey[uKeyColumns-1][3]];
+ tempKey[0][3] ^= S[tempKey[uKeyColumns-1][0]];
+ tempKey[0][0] ^= rcon[rconpointer++];
+
+ if (uKeyColumns != 8)
+ for(j = 1; j < uKeyColumns; j++)
+ for (int k=0;k<4;k++)
+ tempKey[j][k] ^= tempKey[j-1][k];
+ else
+ {
+ for(j = 1; j < uKeyColumns/2; j++)
+ for (int k=0;k<4;k++)
+ tempKey[j][k] ^= tempKey[j-1][k];
+
+ tempKey[uKeyColumns/2][0] ^= S[tempKey[uKeyColumns/2 - 1][0]];
+ tempKey[uKeyColumns/2][1] ^= S[tempKey[uKeyColumns/2 - 1][1]];
+ tempKey[uKeyColumns/2][2] ^= S[tempKey[uKeyColumns/2 - 1][2]];
+ tempKey[uKeyColumns/2][3] ^= S[tempKey[uKeyColumns/2 - 1][3]];
+ for(j = uKeyColumns/2 + 1; j < uKeyColumns; j++)
+ for (int k=0;k<4;k++)
+ tempKey[j][k] ^= tempKey[j-1][k];
+ }
+ for(j = 0; (j < uKeyColumns) && (r <= m_uRounds); )
+ {
+ for(; (j < uKeyColumns) && (t < 4); j++, t++)
+ for (int k=0;k<4;k++)
+ m_expandedKey[r][t][k] = tempKey[j][k];
+ if(t == 4)
+ {
+ r++;
+ t = 0;
+ }
+ }
+ }
+}
+
+void Rijndael::keyEncToDec()
+{
+ for(int r = 1; r < m_uRounds; r++)
+ {
+ byte n_expandedKey[4][4];
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ {
+ byte *w=m_expandedKey[r][j];
+ n_expandedKey[j][i]=U1[w[0]][i]^U2[w[1]][i]^U3[w[2]][i]^U4[w[3]][i];
+ }
+ memcpy(m_expandedKey[r],n_expandedKey,sizeof(m_expandedKey[0]));
+ }
+}
+
+
+#define ff_poly 0x011b
+#define ff_hi 0x80
+
+#define FFinv(x) ((x) ? pow[255 - log[x]]: 0)
+
+#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0)
+#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0)
+#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0)
+#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0)
+#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0)
+#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0)
+#define fwd_affine(x) \
+ (w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8))))
+
+#define inv_affine(x) \
+ (w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8))))
+
+void Rijndael::GenerateTables()
+{
+ unsigned char pow[512],log[256];
+ int i = 0, w = 1;
+ do
+ {
+ pow[i] = (byte)w;
+ pow[i + 255] = (byte)w;
+ log[w] = (byte)i++;
+ w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0);
+ } while (w != 1);
+
+ for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++)
+ {
+ rcon[i] = w;
+ w = (w << 1) ^ (w & ff_hi ? ff_poly : 0);
+ }
+ for(int i = 0; i < 256; ++i)
+ {
+ unsigned char b=S[i]=fwd_affine(FFinv((byte)i));
+ T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b;
+ T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b);
+ T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b);
+ S5[i] = b = FFinv(inv_affine((byte)i));
+ U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b);
+ U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b);
+ U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b);
+ U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b);
+ }
+}
+
+
+#if 0
+static void TestRijndael();
+struct TestRij {TestRij() {TestRijndael();exit(0);}} GlobalTestRij;
+
+// Test CBC encryption according to NIST 800-38A.
+void TestRijndael()
+{
+ byte IV[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
+ byte PT[64]={
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef,
+ 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10,
+ };
+
+ byte Key128[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c};
+ byte Chk128[16]={0x3f,0xf1,0xca,0xa1,0x68,0x1f,0xac,0x09,0x12,0x0e,0xca,0x30,0x75,0x86,0xe1,0xa7};
+ byte Key192[24]={0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52,0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5,0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b};
+ byte Chk192[16]={0x08,0xb0,0xe2,0x79,0x88,0x59,0x88,0x81,0xd9,0x20,0xa9,0xe6,0x4f,0x56,0x15,0xcd};
+ byte Key256[32]={0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4};
+ byte Chk256[16]={0xb2,0xeb,0x05,0xe2,0xc3,0x9b,0xe9,0xfc,0xda,0x6c,0x19,0x07,0x8c,0x6a,0x9d,0x1b};
+ byte *Key[3]={Key128,Key192,Key256};
+ byte *Chk[3]={Chk128,Chk192,Chk256};
+
+ Rijndael rij; // Declare outside of loop to test re-initialization.
+ for (uint L=0;L<3;L++)
+ {
+ byte Out[16];
+ wchar Str[sizeof(Out)*2+1];
+
+ uint KeyLength=128+L*64;
+ rij.Init(true,Key[L],KeyLength,IV);
+ for (uint I=0;I MAXPAR)
+ J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1).
+ }
+ for (int I=MAXPAR;I0;J--)
+ ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D);
+ ShiftReg[0]=gfMult(GXPol[0],D);
+ }
+ for (int I=0;I0;I--)
+ ELPol[I]^=gfMult(M,ELPol[I-1]);
+
+ ErrCount=0;
+
+ // Find roots of error locator polynomial.
+ for (int Root=MAXPAR-DataSize;Root0)
+ for (int I=0;I=0 && DataPosgfSize)
+ E^=0x1100B; // Irreducible field-generator polynomial.
+ }
+
+ // log(0)+log(x) must be outside of usual log table, so we can set it
+ // to 0 and avoid check for 0 in multiplication parameters.
+ gfLog[0]= 2*gfSize;
+ for (uint I=2*gfSize;I<=4*gfSize;I++) // Results for log(0)+log(x).
+ gfExp[I]=0;
+}
+
+
+uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field.
+{
+ return a^b;
+}
+
+
+uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field.
+{
+ return gfExp[gfLog[a]+gfLog[b]];
+}
+
+
+uint RSCoder16::gfInv(uint a) // Inverse element in Galois field.
+{
+ return a==0 ? 0:gfExp[gfSize-gfLog[a]];
+}
+
+
+bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags)
+{
+ ND = DataCount;
+ NR = RecCount;
+ NE = 0;
+
+ Decoding=ValidityFlags!=NULL;
+ if (Decoding)
+ {
+ delete[] ValidFlags;
+ ValidFlags=new bool[ND + NR];
+
+ for (uint I = 0; I < ND + NR; I++)
+ ValidFlags[I]=ValidityFlags[I];
+ for (uint I = 0; I < ND; I++)
+ if (!ValidFlags[I])
+ NE++;
+ uint ValidECC=0;
+ for (uint I = ND; I < ND + NR; I++)
+ if (ValidFlags[I])
+ ValidECC++;
+ if (NE > ValidECC || NE == 0 || ValidECC == 0)
+ return false;
+ }
+ if (ND + NR > gfSize || NR > ND || ND == 0 || NR == 0)
+ return false;
+
+ delete[] MX;
+ if (Decoding)
+ {
+ MX=new uint[NE * ND];
+ MakeDecoderMatrix();
+ InvertDecoderMatrix();
+ }
+ else
+ {
+ MX=new uint[NR * ND];
+ MakeEncoderMatrix();
+ }
+ return true;
+}
+
+
+void RSCoder16::MakeEncoderMatrix()
+{
+ // Create Cauchy encoder generator matrix. Skip trivial "1" diagonal rows,
+ // which would just copy source data to destination.
+ for (uint I = 0; I < NR; I++)
+ for (uint J = 0; J < ND; J++)
+ MX[I * ND + J] = gfInv( gfAdd( (I+ND), J) );
+}
+
+
+void RSCoder16::MakeDecoderMatrix()
+{
+ // Create Cauchy decoder matrix. Skip trivial rows matching valid data
+ // units and containing "1" on main diagonal. Such rows would just copy
+ // source data to destination and they have no real value for us.
+ // Include rows only for broken data units and replace them by first
+ // available valid recovery code rows.
+ for (uint Flag=0, R=ND, Dest=0; Flag < ND; Flag++)
+ if (!ValidFlags[Flag]) // For every broken data unit.
+ {
+ while (!ValidFlags[R]) // Find a valid recovery unit.
+ R++;
+ for (uint J = 0; J < ND; J++) // And place its row to matrix.
+ MX[Dest*ND + J] = gfInv( gfAdd(R,J) );
+ Dest++;
+ R++;
+ }
+}
+
+
+// Apply Gauss–Jordan elimination to find inverse of decoder matrix.
+// We have the square NDxND matrix, but we do not store its trivial
+// diagonal "1" rows matching valid data, so we work with NExND matrix.
+// Our original Cauchy matrix does not contain 0, so we skip search
+// for non-zero pivot.
+void RSCoder16::InvertDecoderMatrix()
+{
+ uint *MI=new uint[NE * ND]; // We'll create inverse matrix here.
+ memset(MI, 0, ND * NE * sizeof(*MI)); // Initialize to identity matrix.
+ for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++)
+ {
+ while (ValidFlags[Kf]) // Skip trivial rows.
+ Kf++;
+ MI[Kr * ND + Kf] = 1; // Set diagonal 1.
+ }
+
+ // Kr is the number of row in our actual reduced NE x ND matrix,
+ // which does not contain trivial diagonal 1 rows.
+ // Kf is the number of row in full ND x ND matrix with all trivial rows
+ // included.
+ for (uint Kr = 0, Kf = 0; Kf < ND; Kr++, Kf++) // Select pivot row.
+ {
+ while (ValidFlags[Kf] && Kf < ND)
+ {
+ // Here we process trivial diagonal 1 rows matching valid data units.
+ // Their processing can be simplified comparing to usual rows.
+ // In full version of elimination we would set MX[I * ND + Kf] to zero
+ // after MI[..]^=, but we do not need it for matrix inversion.
+ for (uint I = 0; I < NE; I++)
+ MI[I * ND + Kf] ^= MX[I * ND + Kf];
+ Kf++;
+ }
+
+ if (Kf == ND)
+ break;
+
+ uint *MXk = MX + Kr * ND; // k-th row of main matrix.
+ uint *MIk = MI + Kr * ND; // k-th row of inversion matrix.
+
+ uint PInv = gfInv( MXk[Kf] ); // Pivot inverse.
+ // Divide the pivot row by pivot, so pivot cell contains 1.
+ for (uint I = 0; I < ND; I++)
+ {
+ MXk[I] = gfMul( MXk[I], PInv );
+ MIk[I] = gfMul( MIk[I], PInv );
+ }
+
+ for (uint I = 0; I < NE; I++)
+ if (I != Kr) // For all rows except containing the pivot cell.
+ {
+ // Apply Gaussian elimination Mij -= Mkj * Mik / pivot.
+ // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik.
+ uint *MXi = MX + I * ND; // i-th row of main matrix.
+ uint *MIi = MI + I * ND; // i-th row of inversion matrix.
+ uint Mik = MXi[Kf]; // Cell in pivot position.
+ for (uint J = 0; J < ND; J++)
+ {
+ MXi[J] ^= gfMul(MXk[J] , Mik);
+ MIi[J] ^= gfMul(MIk[J] , Mik);
+ }
+ }
+ }
+
+ // Copy data to main matrix.
+ for (uint I = 0; I < NE * ND; I++)
+ MX[I] = MI[I];
+
+ delete[] MI;
+}
+
+
+#if 0
+// Multiply matrix to data vector. When encoding, it contains data in Data
+// and stores error correction codes in Out. When decoding it contains
+// broken data followed by ECC in Data and stores recovered data to Out.
+// We do not use this function now, everything is moved to UpdateECC.
+void RSCoder16::Process(const uint *Data, uint *Out)
+{
+ uint ProcData[gfSize];
+
+ for (uint I = 0; I < ND; I++)
+ ProcData[I]=Data[I];
+
+ if (Decoding)
+ {
+ // Replace broken data units with first available valid recovery codes.
+ // 'Data' array must contain recovery codes after data.
+ for (uint I=0, R=ND, Dest=0; I < ND; I++)
+ if (!ValidFlags[I]) // For every broken data unit.
+ {
+ while (!ValidFlags[R]) // Find a valid recovery unit.
+ R++;
+ ProcData[I]=Data[R];
+ R++;
+ }
+ }
+
+ uint H=Decoding ? NE : NR;
+ for (uint I = 0; I < H; I++)
+ {
+ uint R = 0; // Result of matrix row multiplication to data.
+
+ uint *MXi=MX + I * ND;
+ for (uint J = 0; J < ND; J++)
+ R ^= gfMul(MXi[J], ProcData[J]);
+
+ Out[I] = R;
+ }
+}
+#endif
+
+
+// We update ECC in blocks by applying every data block to all ECC blocks.
+// This function applies one data block to one ECC block.
+void RSCoder16::UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize)
+{
+ if (DataNum==0) // Init ECC data.
+ memset(ECC, 0, BlockSize);
+
+ bool DirectAccess;
+#ifdef LITTLE_ENDIAN
+ // We can access data and ECC directly if we have little endian 16 bit uint.
+ DirectAccess=sizeof(ushort)==2;
+#else
+ DirectAccess=false;
+#endif
+
+#ifdef USE_SSE
+ if (DirectAccess && SSE_UpdateECC(DataNum,ECCNum,Data,ECC,BlockSize))
+ return;
+#endif
+
+ if (ECCNum==0)
+ {
+ if (DataLogSize!=BlockSize)
+ {
+ delete[] DataLog;
+ DataLog=new uint[BlockSize];
+ DataLogSize=BlockSize;
+
+ }
+ if (DirectAccess)
+ for (size_t I=0; I>8;
+ ((byte *)&T1L)[I]=gfMul(I<<4,M);
+ ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8;
+ ((byte *)&T2L)[I]=gfMul(I<<8,M);
+ ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8;
+ ((byte *)&T3L)[I]=gfMul(I<<12,M);
+ ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8;
+ }
+
+ size_t Pos=0;
+
+ __m128i LowByteMask=_mm_set1_epi16(0xff); // 00ff00ff...00ff
+ __m128i Low4Mask=_mm_set1_epi8(0xf); // 0f0f0f0f...0f0f
+ __m128i High4Mask=_mm_slli_epi16(Low4Mask,4); // f0f0f0f0...f0f0
+
+ for (; Pos+2*sizeof(__m128i)<=BlockSize; Pos+=2*sizeof(__m128i))
+ {
+ // We process two 128 bit chunks of source data at once.
+ __m128i *D=(__m128i *)(Data+Pos);
+
+ // Place high bytes of both chunks to one variable and low bytes to
+ // another, so we can use the table lookup multiplication for 16 values
+ // 4 bit length each at once.
+ __m128i HighBytes0=_mm_srli_epi16(D[0],8);
+ __m128i LowBytes0=_mm_and_si128(D[0],LowByteMask);
+ __m128i HighBytes1=_mm_srli_epi16(D[1],8);
+ __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask);
+ __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1);
+ __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1);
+
+ // Multiply bits 0..3 of low bytes. Store low and high product bytes
+ // separately in cumulative sum variables.
+ __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask);
+ __m128i LowBytesMultSum=_mm_shuffle_epi8(T0L,LowBytesLow4);
+ __m128i HighBytesMultSum=_mm_shuffle_epi8(T0H,LowBytesLow4);
+
+ // Multiply bits 4..7 of low bytes. Store low and high product bytes separately.
+ __m128i LowBytesHigh4=_mm_and_si128(LowBytes,High4Mask);
+ LowBytesHigh4=_mm_srli_epi16(LowBytesHigh4,4);
+ __m128i LowBytesHigh4MultLow=_mm_shuffle_epi8(T1L,LowBytesHigh4);
+ __m128i LowBytesHigh4MultHigh=_mm_shuffle_epi8(T1H,LowBytesHigh4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh);
+
+ // Multiply bits 0..3 of high bytes. Store low and high product bytes separately.
+ __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask);
+ __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4);
+ __m128i HighBytesLow4MultHigh=_mm_shuffle_epi8(T2H,HighBytesLow4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesLow4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesLow4MultHigh);
+
+ // Multiply bits 4..7 of high bytes. Store low and high product bytes separately.
+ __m128i HighBytesHigh4=_mm_and_si128(HighBytes,High4Mask);
+ HighBytesHigh4=_mm_srli_epi16(HighBytesHigh4,4);
+ __m128i HighBytesHigh4MultLow=_mm_shuffle_epi8(T3L,HighBytesHigh4);
+ __m128i HighBytesHigh4MultHigh=_mm_shuffle_epi8(T3H,HighBytesHigh4);
+
+ // Add new product to existing sum, low and high bytes separately.
+ LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesHigh4MultLow);
+ HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesHigh4MultHigh);
+
+ // Combine separate low and high cumulative sum bytes to 16-bit words.
+ __m128i HighBytesHigh4Mult0=_mm_unpacklo_epi8(LowBytesMultSum,HighBytesMultSum);
+ __m128i HighBytesHigh4Mult1=_mm_unpackhi_epi8(LowBytesMultSum,HighBytesMultSum);
+
+ // Add result to ECC.
+ __m128i *StoreECC=(__m128i *)(ECC+Pos);
+
+ StoreECC[0]=_mm_xor_si128(StoreECC[0],HighBytesHigh4Mult0);
+ StoreECC[1]=_mm_xor_si128(StoreECC[1],HighBytesHigh4Mult1);
+ }
+
+ // If we have non 128 bit aligned data in the end of block, process them
+ // in a usual way. We cannot do the same in the beginning of block,
+ // because Data and ECC can have different alignment offsets.
+ for (; PosSeek(SavePos,SEEK_SET);
+ }
+};
+
+#endif
diff --git a/reference/unrar/scantree.cpp b/reference/unrar/scantree.cpp
new file mode 100644
index 00000000..841a1e92
--- /dev/null
+++ b/reference/unrar/scantree.cpp
@@ -0,0 +1,486 @@
+#include "rar.hpp"
+
+ScanTree::ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs)
+{
+ ScanTree::FileMasks=FileMasks;
+ ScanTree::Recurse=Recurse;
+ ScanTree::GetLinks=GetLinks;
+ ScanTree::GetDirs=GetDirs;
+
+ ScanEntireDisk=false;
+ FolderWildcards=false;
+
+ SetAllMaskDepth=0;
+ *CurMask=0;
+ memset(FindStack,0,sizeof(FindStack));
+ Depth=0;
+ Errors=0;
+ *ErrArcName=0;
+ Cmd=NULL;
+ ErrDirList=NULL;
+ ErrDirSpecPathLength=NULL;
+}
+
+
+ScanTree::~ScanTree()
+{
+ for (int I=Depth;I>=0;I--)
+ if (FindStack[I]!=NULL)
+ delete FindStack[I];
+}
+
+
+SCAN_CODE ScanTree::GetNext(FindData *FD)
+{
+ if (Depth<0)
+ return SCAN_DONE;
+
+#ifndef SILENT
+ uint LoopCount=0;
+#endif
+
+ SCAN_CODE FindCode;
+ while (1)
+ {
+ if (*CurMask==0 && !GetNextMask())
+ return SCAN_DONE;
+
+#ifndef SILENT
+ // Let's return some ticks to system or WinRAR can become irresponsible
+ // while scanning files in command like "winrar a -r arc c:\file.ext".
+ // Also we reset system sleep timer here.
+ if ((++LoopCount & 0x3ff)==0)
+ Wait();
+#endif
+
+ FindCode=FindProc(FD);
+ if (FindCode==SCAN_ERROR)
+ {
+ Errors++;
+ continue;
+ }
+ if (FindCode==SCAN_NEXT)
+ continue;
+ if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS)
+ continue;
+ if (FindCode==SCAN_DONE && GetNextMask())
+ continue;
+ if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS)
+ if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH))
+ continue;
+ break;
+ }
+ return FindCode;
+}
+
+
+// For masks like dir1\dir2*\*.ext in non-recursive mode.
+bool ScanTree::ExpandFolderMask()
+{
+ bool WildcardFound=false;
+ uint SlashPos=0;
+ for (int I=0;CurMask[I]!=0;I++)
+ {
+ if (CurMask[I]=='?' || CurMask[I]=='*')
+ WildcardFound=true;
+ if (WildcardFound && IsPathDiv(CurMask[I]))
+ {
+ // First path separator position after folder wildcard mask.
+ // In case of dir1\dir2*\dir3\name.ext mask it may point not to file
+ // name, so we cannot use PointToName() here.
+ SlashPos=I;
+ break;
+ }
+ }
+
+ wchar Mask[NM];
+ wcsncpyz(Mask,CurMask,ASIZE(Mask));
+ Mask[SlashPos]=0;
+
+ // Prepare the list of all folders matching the wildcard mask.
+ ExpandedFolderList.Reset();
+ FindFile Find;
+ Find.SetMask(Mask);
+ FindData FD;
+ while (Find.Next(&FD))
+ if (FD.IsDir)
+ {
+ wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name));
+
+ // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched
+ // by such mask. Skipping empty dir with dir*\*.* confused some users.
+ wchar *LastMask=PointToName(FD.Name);
+ if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
+ RemoveNameFromPath(FD.Name);
+
+ ExpandedFolderList.AddString(FD.Name);
+ }
+ if (ExpandedFolderList.ItemsCount()==0)
+ return false;
+ // Return the first matching folder name now.
+ ExpandedFolderList.GetString(CurMask,ASIZE(CurMask));
+ return true;
+}
+
+
+// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask
+// and '*\dir2*\file.ext' filter. Masks without folder wildcards are
+// returned as is.
+bool ScanTree::GetFilteredMask()
+{
+ // If we have some matching folders left for non-recursive folder wildcard
+ // mask, we return it here.
+ if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)))
+ return true;
+
+ FolderWildcards=false;
+ FilterList.Reset();
+ if (!FileMasks->GetString(CurMask,ASIZE(CurMask)))
+ return false;
+
+ // Check if folder wildcards present.
+ bool WildcardFound=false;
+ uint FolderWildcardCount=0;
+ uint SlashPos=0;
+ for (int I=0;CurMask[I]!=0;I++)
+ {
+ if (CurMask[I]=='?' || CurMask[I]=='*')
+ WildcardFound=true;
+ if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I]))
+ {
+ if (WildcardFound)
+ {
+ // Calculate a number of folder wildcards in current mask.
+ FolderWildcardCount++;
+ WildcardFound=false;
+ }
+ if (FolderWildcardCount==0)
+ SlashPos=I; // Slash position before first folder wildcard mask.
+ }
+ }
+ if (FolderWildcardCount==0)
+ return true;
+ FolderWildcards=true; // Global folder wildcards flag.
+
+ // If we have only one folder wildcard component and -r is missing or -r-
+ // is specified, prepare matching folders in non-recursive mode.
+ // We assume -r for masks like dir1*\dir2*\file*, because it is complicated
+ // to fast find them using OS file find API call.
+ if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1)
+ return ExpandFolderMask();
+
+ wchar Filter[NM];
+ // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders.
+ wcscpy(Filter,L"*");
+ AddEndSlash(Filter,ASIZE(Filter));
+ // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*'
+ wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos;
+ wcsncatz(Filter,WildName,ASIZE(Filter));
+
+ // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched
+ // by such mask. Skipping empty dir with dir*\*.* confused some users.
+ wchar *LastMask=PointToName(Filter);
+ if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
+ *LastMask=0;
+
+ FilterList.AddString(Filter);
+
+ bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]);
+ if (RelativeDrive)
+ SlashPos++; // Use "d:" instead of "d" for d:* mask.
+
+ CurMask[SlashPos]=0;
+
+ if (!RelativeDrive) // Keep d: mask as is, not convert to d:\*
+ {
+ // We need to append "\*" both for -ep1 to work correctly and to
+ // convert d:\* masks previously truncated to d: back to original form.
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
+ }
+ return true;
+}
+
+
+bool ScanTree::GetNextMask()
+{
+ if (!GetFilteredMask())
+ return false;
+#ifdef _WIN_ALL
+ UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask));
+#endif
+
+ // We wish to scan entire disk if mask like c:\ is specified
+ // regardless of recursion mode. Use c:\*.* mask when need to scan only
+ // the root directory.
+ ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0;
+
+ wchar *Name=PointToName(CurMask);
+ if (*Name==0)
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
+ if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0))
+ {
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
+ }
+ SpecPathLength=Name-CurMask;
+ Depth=0;
+
+ wcscpy(OrigCurMask,CurMask);
+
+ return true;
+}
+
+
+SCAN_CODE ScanTree::FindProc(FindData *FD)
+{
+ if (*CurMask==0)
+ return SCAN_NEXT;
+ bool FastFindFile=false;
+
+ if (FindStack[Depth]==NULL) // No FindFile object for this depth yet.
+ {
+ bool Wildcards=IsWildcard(CurMask);
+
+ // If we have a file name without wildcards, we can try to use
+ // FastFind to optimize speed. For example, in Unix it results in
+ // stat call instead of opendir/readdir/closedir.
+ bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks);
+
+ // Link check is important for NTFS, where links can have "Directory"
+ // attribute, but we do not want to recurse to them in "get links" mode.
+ bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink);
+
+ // SearchAll means that we'll use "*" mask for search, so we'll find
+ // subdirectories and will be able to recurse into them.
+ // We do not use "*" for directories at any level or for files
+ // at top level in recursion mode. We always comrpess the entire directory
+ // if folder wildcard is specified.
+ bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS ||
+ FolderWildcards && Recurse!=RECURSE_DISABLE ||
+ Wildcards && Recurse==RECURSE_WILDCARDS ||
+ ScanEntireDisk && Recurse!=RECURSE_DISABLE);
+ if (Depth==0)
+ SearchAllInRoot=SearchAll;
+ if (SearchAll || Wildcards)
+ {
+ // Create the new FindFile object for wildcard based search.
+ FindStack[Depth]=new FindFile;
+
+ wchar SearchMask[NM];
+ wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask));
+ if (SearchAll)
+ SetName(SearchMask,MASKALL,ASIZE(SearchMask));
+ FindStack[Depth]->SetMask(SearchMask);
+ }
+ else
+ {
+ // Either we failed to fast find or we found a file or we found
+ // a directory in RECURSE_DISABLE mode, so we do not need to scan it.
+ // We can return here and do not need to process further.
+ // We need to process further only if we fast found a directory.
+ if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE)
+ {
+ // Return SCAN_SUCCESS if we found a file.
+ SCAN_CODE RetCode=SCAN_SUCCESS;
+
+ if (!FindCode)
+ {
+ // Return SCAN_ERROR if problem is more serious than just
+ // "file not found".
+ RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT;
+
+ // If we failed to find an object, but our current mask is excluded,
+ // we skip this object and avoid indicating an error.
+ if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
+ RetCode=SCAN_NEXT;
+ else
+ {
+ ErrHandler.OpenErrorMsg(ErrArcName,CurMask);
+ // User asked to return RARX_NOFILES and not RARX_OPEN here.
+ ErrHandler.SetErrorCode(RARX_NOFILES);
+ }
+ }
+
+ // If we searched only for one file or directory in "fast find"
+ // (without a wildcard) mode, let's set masks to zero,
+ // so calling function will know that current mask is used
+ // and next one must be read from mask list for next call.
+ // It is not necessary for directories, because even in "fast find"
+ // mode, directory recursing will quit by (Depth < 0) condition,
+ // which returns SCAN_DONE to calling function.
+ *CurMask=0;
+
+ return RetCode;
+ }
+
+ // We found a directory using only FindFile::FastFind function.
+ FastFindFile=true;
+ }
+ }
+
+ if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks))
+ {
+ // We cannot find anything more in directory either because of
+ // some error or just as result of all directory entries already read.
+
+ bool Error=FD->Error;
+ if (Error)
+ ScanError(Error);
+
+ wchar DirName[NM];
+ *DirName=0;
+
+ // Going to at least one directory level higher.
+ delete FindStack[Depth];
+ FindStack[Depth--]=NULL;
+ while (Depth>=0 && FindStack[Depth]==NULL)
+ Depth--;
+ if (Depth < 0)
+ {
+ // Directories scanned both in normal and FastFindFile mode,
+ // finally exit from scan here, by (Depth < 0) condition.
+
+ if (Error)
+ Errors++;
+ return SCAN_DONE;
+ }
+
+ wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER);
+ if (Slash!=NULL)
+ {
+ wchar Mask[NM];
+ wcscpy(Mask,Slash);
+ if (DepthIsDir)
+ {
+ FD->Flags|=FDDF_SECONDDIR;
+ return Error ? SCAN_ERROR:SCAN_SUCCESS;
+ }
+ return Error ? SCAN_ERROR:SCAN_NEXT;
+ }
+
+ // Link check is required for NTFS links, not for Unix.
+ if (FD->IsDir && (!GetLinks || !FD->IsLink))
+ {
+ // If we found the directory in top (Depth==0) directory
+ // and if we are not in "fast find" (directory name only as argument)
+ // or in recurse (SearchAll was set when opening the top directory) mode,
+ // we do not recurse into this directory. We either return it by itself
+ // or skip it.
+ if (!FastFindFile && Depth==0 && !SearchAllInRoot)
+ return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT;
+
+ // Let's check if directory name is excluded, so we do not waste
+ // time searching in directory, which will be excluded anyway.
+ if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) ||
+ Cmd->ExclDirByAttr(FD->FileAttr)))
+ {
+ // If we are here in "fast find" mode, it means that entire directory
+ // specified in command line is excluded. Then we need to return
+ // SCAN_DONE to go to next mask and avoid the infinite loop
+ // in GetNext() function. Such loop would be possible in case of
+ // SCAN_NEXT code and "rar a arc dir -xdir" command.
+
+ return FastFindFile ? SCAN_DONE:SCAN_NEXT;
+ }
+
+ wchar Mask[NM];
+
+ wcscpy(Mask,FastFindFile ? MASKALL:PointToName(CurMask));
+ wcscpy(CurMask,FD->Name);
+
+ if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1)
+ {
+ uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask);
+ return SCAN_ERROR;
+ }
+
+ AddEndSlash(CurMask,ASIZE(CurMask));
+ wcsncatz(CurMask,Mask,ASIZE(CurMask));
+
+ Depth++;
+
+ // We need to use OrigCurMask for depths less than SetAllMaskDepth
+ // and "*" for depths equal or larger than SetAllMaskDepth.
+ // It is important when "fast finding" directories at Depth > 0.
+ // For example, if current directory is RootFolder and we compress
+ // the following directories structure:
+ // RootFolder
+ // +--Folder1
+ // | +--Folder2
+ // | +--Folder3
+ // +--Folder4
+ // with 'rar a -r arcname Folder2' command, rar could add not only
+ // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using
+ // "*" mask at all levels. We need to use "*" mask inside of Folder2,
+ // but return to "Folder2" mask when completing scanning Folder2.
+ // We can rewrite SearchAll expression above to avoid fast finding
+ // directories at Depth > 0, but then 'rar a -r arcname Folder2'
+ // will add the empty Folder2 and do not add its contents.
+
+ if (FastFindFile)
+ SetAllMaskDepth=Depth;
+ }
+ if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES))
+ return SCAN_NEXT;
+
+ return SCAN_SUCCESS;
+}
+
+
+void ScanTree::ScanError(bool &Error)
+{
+#ifdef _WIN_ALL
+ if (Error)
+ {
+ // Get attributes of parent folder and do not display an error
+ // if it is reparse point. We cannot scan contents of standard
+ // Windows reparse points like "C:\Documents and Settings"
+ // and we do not want to issue numerous useless errors for them.
+ // We cannot just check FD->FileAttr here, it can be undefined
+ // if we process "folder\*" mask or if we process "folder" mask,
+ // but "folder" is inaccessible.
+ wchar *Slash=PointToName(CurMask);
+ if (Slash>CurMask)
+ {
+ *(Slash-1)=0;
+ DWORD Attr=GetFileAttributes(CurMask);
+ *(Slash-1)=CPATHDIVIDER;
+ if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
+ Error=false;
+ }
+
+ // Do not display an error if we cannot scan contents of
+ // "System Volume Information" folder. Normally it is not accessible.
+ if (wcsstr(CurMask,L"System Volume Information\\")!=NULL)
+ Error=false;
+ }
+#endif
+
+ if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
+ Error=false;
+
+ if (Error)
+ {
+ if (ErrDirList!=NULL)
+ ErrDirList->AddString(CurMask);
+ if (ErrDirSpecPathLength!=NULL)
+ ErrDirSpecPathLength->Push((uint)SpecPathLength);
+ wchar FullName[NM];
+ // This conversion works for wildcard masks too.
+ ConvertNameToFull(CurMask,FullName,ASIZE(FullName));
+ uiMsg(UIERROR_DIRSCAN,FullName);
+ ErrHandler.SysErrMsg();
+ }
+}
diff --git a/reference/unrar/scantree.hpp b/reference/unrar/scantree.hpp
new file mode 100644
index 00000000..40a6d849
--- /dev/null
+++ b/reference/unrar/scantree.hpp
@@ -0,0 +1,78 @@
+#ifndef _RAR_SCANTREE_
+#define _RAR_SCANTREE_
+
+enum SCAN_DIRS
+{
+ SCAN_SKIPDIRS, // Skip directories, but recurse for files if recursion mode is enabled.
+ SCAN_GETDIRS, // Get subdirectories in recurse mode.
+ SCAN_GETDIRSTWICE, // Get the directory name both before and after the list of files it contains.
+ SCAN_GETCURDIRS // Get subdirectories in current directory even in RECURSE_NONE mode.
+};
+
+enum SCAN_CODE { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT };
+
+#define MAXSCANDEPTH (NM/2)
+
+class CommandData;
+
+class ScanTree
+{
+ private:
+ bool ExpandFolderMask();
+ bool GetFilteredMask();
+ bool GetNextMask();
+ SCAN_CODE FindProc(FindData *FD);
+ void ScanError(bool &Error);
+
+ FindFile *FindStack[MAXSCANDEPTH];
+ int Depth;
+
+ int SetAllMaskDepth;
+
+ StringList *FileMasks;
+ RECURSE_MODE Recurse;
+ bool GetLinks;
+ SCAN_DIRS GetDirs;
+ int Errors;
+
+ // Set when processing paths like c:\ (root directory without wildcards).
+ bool ScanEntireDisk;
+
+ wchar CurMask[NM];
+ wchar OrigCurMask[NM];
+
+ // Store all folder masks generated from folder wildcard mask in non-recursive mode.
+ StringList ExpandedFolderList;
+
+ // Store a filter string for folder wildcard in recursive mode.
+ StringList FilterList;
+
+ // Save the list of unreadable dirs here.
+ StringList *ErrDirList;
+ Array *ErrDirSpecPathLength;
+
+ // Set if processing a folder wildcard mask.
+ bool FolderWildcards;
+
+ bool SearchAllInRoot;
+ size_t SpecPathLength;
+
+ wchar ErrArcName[NM];
+
+ CommandData *Cmd;
+ public:
+ ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs);
+ ~ScanTree();
+ SCAN_CODE GetNext(FindData *FindData);
+ size_t GetSpecPathLength() {return SpecPathLength;};
+ int GetErrors() {return Errors;};
+ void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));}
+ void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;}
+ void SetErrDirList(StringList *List,Array *Lengths)
+ {
+ ErrDirList=List;
+ ErrDirSpecPathLength=Lengths;
+ }
+};
+
+#endif
diff --git a/reference/unrar/secpassword.cpp b/reference/unrar/secpassword.cpp
new file mode 100644
index 00000000..4865b3fd
--- /dev/null
+++ b/reference/unrar/secpassword.cpp
@@ -0,0 +1,216 @@
+#include "rar.hpp"
+
+#if defined(_WIN_ALL)
+typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
+typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags);
+
+#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE
+#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16
+#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00
+#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01
+#endif
+
+class CryptLoader
+{
+ private:
+ HMODULE hCrypt;
+ bool LoadCalled;
+ public:
+ CryptLoader()
+ {
+ hCrypt=NULL;
+ pCryptProtectMemory=NULL;
+ pCryptUnprotectMemory=NULL;
+ LoadCalled=false;
+ }
+ ~CryptLoader()
+ {
+ if (hCrypt!=NULL)
+ FreeLibrary(hCrypt);
+ hCrypt=NULL;
+ pCryptProtectMemory=NULL;
+ pCryptUnprotectMemory=NULL;
+ };
+ void Load()
+ {
+ if (!LoadCalled)
+ {
+ hCrypt = LoadSysLibrary(L"Crypt32.dll");
+ if (hCrypt != NULL)
+ {
+ // Available since Vista.
+ pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory");
+ pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory");
+ }
+ LoadCalled=true;
+ }
+ }
+
+ CRYPTPROTECTMEMORY pCryptProtectMemory;
+ CRYPTUNPROTECTMEMORY pCryptUnprotectMemory;
+};
+
+// We need to call FreeLibrary when RAR is exiting.
+CryptLoader GlobalCryptLoader;
+#endif
+
+SecPassword::SecPassword()
+{
+ CrossProcess=false;
+ Set(L"");
+}
+
+
+SecPassword::~SecPassword()
+{
+ Clean();
+}
+
+
+void SecPassword::Clean()
+{
+ PasswordSet=false;
+ cleandata(Password,sizeof(Password));
+}
+
+
+// When we call memset in end of function to clean local variables
+// for security reason, compiler optimizer can remove such call.
+// So we use our own function for this purpose.
+void cleandata(void *data,size_t size)
+{
+ if (data==NULL || size==0)
+ return;
+#if defined(_WIN_ALL) && defined(_MSC_VER)
+ SecureZeroMemory(data,size);
+#else
+ // 'volatile' is required. Otherwise optimizers can remove this function
+ // if cleaning local variables, which are not used after that.
+ volatile byte *d = (volatile byte *)data;
+ for (size_t i=0;i parameter, so we need to take into account both sizes.
+ memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst));
+ SecHideData(Dst,DstSize*sizeof(*Dst),Encode,CrossProcess);
+}
+
+
+void SecPassword::Get(wchar *Psw,size_t MaxSize)
+{
+ if (PasswordSet)
+ {
+ Process(Password,ASIZE(Password),Psw,MaxSize,false);
+ Psw[MaxSize-1]=0;
+ }
+ else
+ *Psw=0;
+}
+
+
+
+
+void SecPassword::Set(const wchar *Psw)
+{
+ if (*Psw==0)
+ {
+ PasswordSet=false;
+ memset(Password,0,sizeof(Password));
+ }
+ else
+ {
+ PasswordSet=true;
+ Process(Psw,wcslen(Psw)+1,Password,ASIZE(Password),true);
+ }
+}
+
+
+size_t SecPassword::Length()
+{
+ wchar Plain[MAXPASSWORD];
+ Get(Plain,ASIZE(Plain));
+ size_t Length=wcslen(Plain);
+ cleandata(Plain,ASIZE(Plain));
+ return Length;
+}
+
+
+bool SecPassword::operator == (SecPassword &psw)
+{
+ // We cannot compare encoded data directly, because there is no guarantee
+ // than encryption function will always produce the same result for same
+ // data (salt?) and because we do not clean the rest of password buffer
+ // after trailing zero before encoding password. So we decode first.
+ wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD];
+ Get(Plain1,ASIZE(Plain1));
+ psw.Get(Plain2,ASIZE(Plain2));
+ bool Result=wcscmp(Plain1,Plain2)==0;
+ cleandata(Plain1,ASIZE(Plain1));
+ cleandata(Plain2,ASIZE(Plain2));
+ return Result;
+}
+
+
+void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess)
+{
+ // CryptProtectMemory is not available in UWP and CryptProtectData
+ // increases data size not allowing in place conversion.
+#if defined(_WIN_ALL)
+ // Try to utilize the secure Crypt[Un]ProtectMemory if possible.
+ if (GlobalCryptLoader.pCryptProtectMemory==NULL)
+ GlobalCryptLoader.Load();
+ size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE;
+ DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS;
+ if (Encode)
+ {
+ if (GlobalCryptLoader.pCryptProtectMemory!=NULL)
+ {
+ if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags))
+ {
+ ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed");
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return;
+ }
+ }
+ else
+ {
+ if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL)
+ {
+ if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags))
+ {
+ ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed");
+ ErrHandler.SysErrMsg();
+ ErrHandler.Exit(RARX_FATAL);
+ }
+ return;
+ }
+ }
+#endif
+
+ // CryptProtectMemory is not available, so only slightly obfuscate data.
+ uint Key;
+#ifdef _WIN_ALL
+ Key=GetCurrentProcessId();
+#elif defined(_UNIX)
+ Key=getpid();
+#else
+ Key=0; // Just an arbitrary value.
+#endif
+
+ for (size_t I=0;I
+100% Public Domain
+*/
+
+#ifndef SFX_MODULE
+#define SHA1_UNROLL
+#endif
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifdef LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = ByteSwap32(block->l[i]))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rotl32(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);}
+#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);}
+#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rotl32(v,5);w=rotl32(w,30);}
+#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rotl32(v,5);w=rotl32(w,30);}
+#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rotl32(v,5);w=rotl32(w,30);}
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+void SHA1Transform(uint32 state[5], uint32 workspace[16], const byte buffer[64], bool inplace)
+{
+ uint32 a, b, c, d, e;
+
+ union CHAR64LONG16
+ {
+ unsigned char c[64];
+ uint32 l[16];
+ } *block;
+
+ if (inplace)
+ block = (CHAR64LONG16*)buffer;
+ else
+ {
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+ }
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+#ifdef SHA1_UNROLL
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+#else
+ for (uint I=0;;I+=5)
+ {
+ R0(a,b,c,d,e, I+0); if (I==15) break;
+ R0(e,a,b,c,d, I+1); R0(d,e,a,b,c, I+2);
+ R0(c,d,e,a,b, I+3); R0(b,c,d,e,a, I+4);
+ }
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ for (uint I=20;I<=35;I+=5)
+ {
+ R2(a,b,c,d,e,I+0); R2(e,a,b,c,d,I+1); R2(d,e,a,b,c,I+2);
+ R2(c,d,e,a,b,I+3); R2(b,c,d,e,a,I+4);
+ }
+ for (uint I=40;I<=55;I+=5)
+ {
+ R3(a,b,c,d,e,I+0); R3(e,a,b,c,d,I+1); R3(d,e,a,b,c,I+2);
+ R3(c,d,e,a,b,I+3); R3(b,c,d,e,a,I+4);
+ }
+ for (uint I=60;I<=75;I+=5)
+ {
+ R4(a,b,c,d,e,I+0); R4(e,a,b,c,d,I+1); R4(d,e,a,b,c,I+2);
+ R4(c,d,e,a,b,I+3); R4(b,c,d,e,a,I+4);
+ }
+#endif
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+}
+
+
+/* Initialize new context */
+void sha1_init(sha1_context* context)
+{
+ context->count = 0;
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+}
+
+
+/* Run your data through this. */
+void sha1_process( sha1_context * context, const unsigned char * data, size_t len)
+{
+ size_t i, j = (size_t)(context->count & 63);
+ context->count += len;
+
+ if ((j + len) > 63)
+ {
+ memcpy(context->buffer+j, data, (i = 64-j));
+ uint32 workspace[16];
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ for ( ; i + 63 < len; i += 64)
+ SHA1Transform(context->state, workspace, data+i, false);
+ j = 0;
+ }
+ else
+ i = 0;
+ if (len > i)
+ memcpy(context->buffer+j, data+i, len - i);
+}
+
+
+void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len)
+{
+ size_t i, j = (size_t)(context->count & 63);
+ context->count += len;
+
+ if ((j + len) > 63)
+ {
+ memcpy(context->buffer+j, data, (i = 64-j));
+ uint32 workspace[16];
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ for ( ; i + 63 < len; i += 64)
+ {
+ SHA1Transform(context->state, workspace, data+i, false);
+ for (uint k = 0; k < 16; k++)
+ RawPut4(workspace[k],(void*)(data+i+k*4));
+ }
+ j = 0;
+ }
+ else
+ i = 0;
+ if (len > i)
+ memcpy(context->buffer+j, data+i, len - i);
+}
+
+
+/* Add padding and return the message digest. */
+void sha1_done( sha1_context* context, uint32 digest[5])
+{
+ uint32 workspace[16];
+ uint64 BitLength = context->count * 8;
+ uint BufPos = (uint)context->count & 0x3f;
+ context->buffer[BufPos++] = 0x80; // Padding the message with "1" bit.
+
+ if (BufPos!=56) // We need 56 bytes block followed by 8 byte length.
+ {
+ if (BufPos>56)
+ {
+ while (BufPos<64)
+ context->buffer[BufPos++] = 0;
+ BufPos=0;
+ }
+ if (BufPos==0)
+ SHA1Transform(context->state, workspace, context->buffer, true);
+ memset(context->buffer+BufPos,0,56-BufPos);
+ }
+
+ RawPutBE4((uint32)(BitLength>>32), context->buffer + 56);
+ RawPutBE4((uint32)(BitLength), context->buffer + 60);
+
+ SHA1Transform(context->state, workspace, context->buffer, true);
+
+ for (uint i = 0; i < 5; i++)
+ digest[i] = context->state[i];
+
+ /* Wipe variables */
+ sha1_init(context);
+}
+
+
diff --git a/reference/unrar/sha1.hpp b/reference/unrar/sha1.hpp
new file mode 100644
index 00000000..7c0b7fb7
--- /dev/null
+++ b/reference/unrar/sha1.hpp
@@ -0,0 +1,15 @@
+#ifndef _RAR_SHA1_
+#define _RAR_SHA1_
+
+typedef struct {
+ uint32 state[5];
+ uint64 count;
+ unsigned char buffer[64];
+} sha1_context;
+
+void sha1_init( sha1_context * c );
+void sha1_process(sha1_context * c, const byte *data, size_t len);
+void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len);
+void sha1_done( sha1_context * c, uint32 digest[5] );
+
+#endif
diff --git a/reference/unrar/sha256.cpp b/reference/unrar/sha256.cpp
new file mode 100644
index 00000000..f90d2c09
--- /dev/null
+++ b/reference/unrar/sha256.cpp
@@ -0,0 +1,148 @@
+#include "rar.hpp"
+#include "sha256.hpp"
+
+static const uint32 K[64] =
+{
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+// SHA-256 functions. We could optimize Ch and Maj a little,
+// but with no visible speed benefit.
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+
+// Sigma functions.
+#define Sg0(x) (rotr32(x, 2) ^ rotr32(x,13) ^ rotr32(x, 22))
+#define Sg1(x) (rotr32(x, 6) ^ rotr32(x,11) ^ rotr32(x, 25))
+#define sg0(x) (rotr32(x, 7) ^ rotr32(x,18) ^ (x >> 3))
+#define sg1(x) (rotr32(x,17) ^ rotr32(x,19) ^ (x >> 10))
+
+void sha256_init(sha256_context *ctx)
+{
+ ctx->H[0] = 0x6a09e667; // Set the initial hash value.
+ ctx->H[1] = 0xbb67ae85;
+ ctx->H[2] = 0x3c6ef372;
+ ctx->H[3] = 0xa54ff53a;
+ ctx->H[4] = 0x510e527f;
+ ctx->H[5] = 0x9b05688c;
+ ctx->H[6] = 0x1f83d9ab;
+ ctx->H[7] = 0x5be0cd19;
+ ctx->Count = 0; // Processed data counter.
+}
+
+
+static void sha256_transform(sha256_context *ctx)
+{
+ uint32 W[64]; // Words of message schedule.
+ uint32 v[8]; // FIPS a, b, c, d, e, f, g, h working variables.
+
+ // Prepare message schedule.
+ for (uint I = 0; I < 16; I++)
+ W[I] = RawGetBE4(ctx->Buffer + I * 4);
+ for (uint I = 16; I < 64; I++)
+ W[I] = sg1(W[I-2]) + W[I-7] + sg0(W[I-15]) + W[I-16];
+
+ uint32 *H=ctx->H;
+ v[0]=H[0]; v[1]=H[1]; v[2]=H[2]; v[3]=H[3];
+ v[4]=H[4]; v[5]=H[5]; v[6]=H[6]; v[7]=H[7];
+
+ for (uint I = 0; I < 64; I++)
+ {
+ uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I];
+
+ // It is possible to eliminate variable copying if we unroll loop
+ // and rename variables every time. But my test did not show any speed
+ // gain on i7 for such full or partial unrolling.
+ v[7] = v[6];
+ v[6] = v[5];
+ v[5] = v[4];
+ v[4] = v[3] + T1;
+
+ // It works a little faster when moved here from beginning of loop.
+ uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]);
+
+ v[3] = v[2];
+ v[2] = v[1];
+ v[1] = v[0];
+ v[0] = T1 + T2;
+ }
+
+ H[0]+=v[0]; H[1]+=v[1]; H[2]+=v[2]; H[3]+=v[3];
+ H[4]+=v[4]; H[5]+=v[5]; H[6]+=v[6]; H[7]+=v[7];
+}
+
+
+void sha256_process(sha256_context *ctx, const void *Data, size_t Size)
+{
+ const byte *Src=(const byte *)Data;
+ size_t BufPos = (uint)ctx->Count & 0x3f;
+ ctx->Count+=Size;
+ while (Size > 0)
+ {
+ size_t BufSpace=sizeof(ctx->Buffer)-BufPos;
+ size_t CopySize=Size>BufSpace ? BufSpace:Size;
+
+ memcpy(ctx->Buffer+BufPos,Src,CopySize);
+
+ Src+=CopySize;
+ BufPos+=CopySize;
+ Size-=CopySize;
+ if (BufPos == 64)
+ {
+ BufPos = 0;
+ sha256_transform(ctx);
+ }
+ }
+}
+
+
+void sha256_done(sha256_context *ctx, byte *Digest)
+{
+ uint64 BitLength = ctx->Count * 8;
+ uint BufPos = (uint)ctx->Count & 0x3f;
+ ctx->Buffer[BufPos++] = 0x80; // Padding the message with "1" bit.
+
+ if (BufPos!=56) // We need 56 bytes block followed by 8 byte length.
+ {
+ if (BufPos>56)
+ {
+ while (BufPos<64)
+ ctx->Buffer[BufPos++] = 0;
+ BufPos=0;
+ }
+ if (BufPos==0)
+ sha256_transform(ctx);
+ memset(ctx->Buffer+BufPos,0,56-BufPos);
+ }
+
+ RawPutBE4((uint32)(BitLength>>32), ctx->Buffer + 56);
+ RawPutBE4((uint32)(BitLength), ctx->Buffer + 60);
+
+ sha256_transform(ctx);
+
+ RawPutBE4(ctx->H[0], Digest + 0);
+ RawPutBE4(ctx->H[1], Digest + 4);
+ RawPutBE4(ctx->H[2], Digest + 8);
+ RawPutBE4(ctx->H[3], Digest + 12);
+ RawPutBE4(ctx->H[4], Digest + 16);
+ RawPutBE4(ctx->H[5], Digest + 20);
+ RawPutBE4(ctx->H[6], Digest + 24);
+ RawPutBE4(ctx->H[7], Digest + 28);
+
+ sha256_init(ctx);
+}
diff --git a/reference/unrar/sha256.hpp b/reference/unrar/sha256.hpp
new file mode 100644
index 00000000..b6837e76
--- /dev/null
+++ b/reference/unrar/sha256.hpp
@@ -0,0 +1,17 @@
+#ifndef _RAR_SHA256_
+#define _RAR_SHA256_
+
+#define SHA256_DIGEST_SIZE 32
+
+typedef struct
+{
+ uint32 H[8];
+ uint64 Count;
+ byte Buffer[64];
+} sha256_context;
+
+void sha256_init(sha256_context *ctx);
+void sha256_process(sha256_context *ctx, const void *Data, size_t Size);
+void sha256_done(sha256_context *ctx, byte *Digest);
+
+#endif
diff --git a/reference/unrar/smallfn.cpp b/reference/unrar/smallfn.cpp
new file mode 100644
index 00000000..81259d02
--- /dev/null
+++ b/reference/unrar/smallfn.cpp
@@ -0,0 +1,19 @@
+#include "rar.hpp"
+
+int ToPercent(int64 N1,int64 N2)
+{
+ if (N2SrcLength)
+ DestSize=SrcLength;
+ OemToCharBuffA(Src,Dest,(DWORD)DestSize);
+ Dest[DestSize-1]=0;
+#else
+ if (Dest!=Src)
+ strncpyz(Dest,Src,DestSize);
+#endif
+}
+
+
+// Convert archived names to Unicode. Allow user to select a code page in GUI.
+void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding)
+{
+#if defined(_WIN_ALL) // Console Windows RAR.
+ if (Encoding==ACTW_UTF8)
+ UtfToWide(Src,Dest,DestSize);
+ else
+ {
+ char NameA[NM];
+ if (Encoding==ACTW_OEM)
+ {
+ IntToExt(Src,NameA,ASIZE(NameA));
+ Src=NameA;
+ }
+ CharToWide(Src,Dest,DestSize);
+ }
+#else // RAR for Unix.
+ if (Encoding==ACTW_UTF8)
+ UtfToWide(Src,Dest,DestSize);
+ else
+ CharToWide(Src,Dest,DestSize);
+#endif
+ // Ensure that we return a zero terminate string for security reason.
+ // While [Jni]CharToWide might already do it, be protected in case of future
+ // changes in these functions.
+ if (DestSize>0)
+ Dest[DestSize-1]=0;
+}
+
+
+int stricomp(const char *s1,const char *s2)
+{
+#ifdef _WIN_ALL
+ return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2;
+#else
+ while (toupper(*s1)==toupper(*s2))
+ {
+ if (*s1==0)
+ return 0;
+ s1++;
+ s2++;
+ }
+ return s1 < s2 ? -1 : 1;
+#endif
+}
+
+
+int strnicomp(const char *s1,const char *s2,size_t n)
+{
+#ifdef _WIN_ALL
+ // If we specify 'n' exceeding the actual string length, CompareString goes
+ // beyond the trailing zero and compares garbage. So we need to limit 'n'
+ // to real string length.
+ // It is important to use strnlen (or memchr(...,0)) instead of strlen,
+ // because data can be not zero terminated.
+ size_t l1=Min(strnlen(s1,n),n);
+ size_t l2=Min(strnlen(s2,n),n);
+ return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2;
+#else
+ if (n==0)
+ return 0;
+ while (toupper(*s1)==toupper(*s2))
+ {
+ if (*s1==0 || --n==0)
+ return 0;
+ s1++;
+ s2++;
+ }
+ return s1 < s2 ? -1 : 1;
+#endif
+}
+
+
+wchar* RemoveEOL(wchar *Str)
+{
+ for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--)
+ Str[I]=0;
+ return Str;
+}
+
+
+wchar* RemoveLF(wchar *Str)
+{
+ for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--)
+ Str[I]=0;
+ return Str;
+}
+
+
+unsigned char loctolower(unsigned char ch)
+{
+#if defined(_WIN_ALL)
+ // Convert to LPARAM first to avoid a warning in 64 bit mode.
+ return (int)(LPARAM)CharLowerA((LPSTR)ch);
+#else
+ return tolower(ch);
+#endif
+}
+
+
+unsigned char loctoupper(unsigned char ch)
+{
+#if defined(_WIN_ALL)
+ // Convert to LPARAM first to avoid a warning in 64 bit mode.
+ return (int)(LPARAM)CharUpperA((LPSTR)ch);
+#else
+ return toupper(ch);
+#endif
+}
+
+
+// toupper with English only results if English input is provided.
+// It avoids Turkish (small i) -> (big I with dot) conversion problem.
+// We do not define 'ch' as 'int' to avoid necessity to cast all
+// signed chars passed to this function to unsigned char.
+unsigned char etoupper(unsigned char ch)
+{
+ if (ch=='i')
+ return 'I';
+ return toupper(ch);
+}
+
+
+// Unicode version of etoupper.
+wchar etoupperw(wchar ch)
+{
+ if (ch=='i')
+ return 'I';
+ return toupperw(ch);
+}
+
+
+// We do not want to cast every signed char to unsigned when passing to
+// isdigit, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard isdigit.
+bool IsDigit(int ch)
+{
+ return ch>='0' && ch<='9';
+}
+
+
+// We do not want to cast every signed char to unsigned when passing to
+// isspace, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard isspace.
+bool IsSpace(int ch)
+{
+ return ch==' ' || ch=='\t';
+}
+
+
+// We do not want to cast every signed char to unsigned when passing to
+// isalpha, so we implement the replacement. Shall work for Unicode too.
+// If chars are signed, conversion from char to int could generate negative
+// values, resulting in undefined behavior in standard function.
+bool IsAlpha(int ch)
+{
+ return ch>='A' && ch<='Z' || ch>='a' && ch<='z';
+}
+
+
+
+
+void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize)
+{
+ uint A=0,W=0; // ASCII and Unicode hex output positions.
+ for (uint I=0;I> 4;
+ uint Low=Bin[I] & 0xf;
+ uint HighHex=High>9 ? 'a'+High-10:'0'+High;
+ uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low;
+ if (HexA!=NULL && A0)
+ HexA[A]=0;
+ if (HexW!=NULL && HexSize>0)
+ HexW[W]=0;
+}
+
+
+#ifndef SFX_MODULE
+uint GetDigits(uint Number)
+{
+ uint Digits=1;
+ while (Number>=10)
+ {
+ Number/=10;
+ Digits++;
+ }
+ return Digits;
+}
+#endif
+
+
+bool LowAscii(const char *Str)
+{
+ for (int I=0;Str[I]!=0;I++)
+ if ((byte)Str[I]<32 || (byte)Str[I]>127)
+ return false;
+ return true;
+}
+
+
+bool LowAscii(const wchar *Str)
+{
+ for (int I=0;Str[I]!=0;I++)
+ {
+ // We convert wchar_t to uint just in case if some compiler
+ // uses signed wchar_t.
+ if ((uint)Str[I]<32 || (uint)Str[I]>127)
+ return false;
+ }
+ return true;
+}
+
+
+int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison.
+{
+#if defined(_UNIX)
+ return wcscmp(s1,s2);
+#else
+ return wcsicomp(s1,s2);
+#endif
+}
+
+
+int wcsnicompc(const wchar *s1,const wchar *s2,size_t n)
+{
+#if defined(_UNIX)
+ return wcsncmp(s1,s2,n);
+#else
+ return wcsnicomp(s1,s2,n);
+#endif
+}
+
+
+// Safe strncpy: copies maxlen-1 max and always returns zero terminated dest.
+char* strncpyz(char *dest, const char *src, size_t maxlen)
+{
+ if (maxlen>0)
+ {
+ strncpy(dest,src,maxlen-1);
+ dest[maxlen-1]=0;
+ }
+ return dest;
+}
+
+
+// Safe wcsncpy: copies maxlen-1 max and always returns zero terminated dest.
+wchar* wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
+{
+ if (maxlen>0)
+ {
+ wcsncpy(dest,src,maxlen-1);
+ dest[maxlen-1]=0;
+ }
+ return dest;
+}
+
+
+// Safe strncat: resulting dest length cannot exceed maxlen and dest
+// is always zero terminated. Note that 'maxlen' parameter defines the entire
+// dest buffer size and is not compatible with standard strncat.
+char* strncatz(char* dest, const char* src, size_t maxlen)
+{
+ size_t Length = strlen(dest);
+ int avail=int(maxlen - Length - 1);
+ if (avail > 0)
+ strncat(dest, src, avail);
+ return dest;
+}
+
+
+// Safe wcsncat: resulting dest length cannot exceed maxlen and dest
+// is always zero terminated. Note that 'maxlen' parameter defines the entire
+// dest buffer size and is not compatible with standard wcsncat.
+wchar* wcsncatz(wchar* dest, const wchar* src, size_t maxlen)
+{
+ size_t Length = wcslen(dest);
+ int avail=int(maxlen - Length - 1);
+ if (avail > 0)
+ wcsncat(dest, src, avail);
+ return dest;
+}
+
+
+void itoa(int64 n,char *Str,size_t MaxSize)
+{
+ char NumStr[50];
+ size_t Pos=0;
+
+ int Neg=n < 0 ? 1 : 0;
+ if (Neg)
+ n=-n;
+
+ do
+ {
+ if (Pos+1>=MaxSize-Neg)
+ break;
+ NumStr[Pos++]=char(n%10)+'0';
+ n=n/10;
+ } while (n!=0);
+
+ if (Neg)
+ NumStr[Pos++]='-';
+
+ for (size_t I=0;I=MaxSize-Neg)
+ break;
+ NumStr[Pos++]=wchar(n%10)+'0';
+ n=n/10;
+ } while (n!=0);
+
+ if (Neg)
+ NumStr[Pos++]='-';
+
+ for (size_t I=0;I