diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/include/unrar')
152 files changed, 30247 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/OpenMPT.txt b/Src/external_dependencies/openmpt-trunk/include/unrar/OpenMPT.txt new file mode 100644 index 00000000..af3655cf --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/OpenMPT.txt @@ -0,0 +1,18 @@ +This folder is based on https://rarlab.com/rar/unrarsrc-6.1.6.tar.gz
+
+All modifications to existing files have been done in a manner so that no
+existing lines were edited; only new lines were added.
+os.hpp has been modified to support Clang/LLVM for Windows.
+Work-arounds for tiny problems with Clang/C2 have been implemented.
+Added lines can be found by looking for "// OPENMPT ADDITION" comments.
+
+Optimizations have been done to elimiate effectively dead code that would
+never be used in OpenMPT's context.
+
+The following files have been modified:
+arcread.cpp, extract.cpp, filcreat.cpp, file.cpp, filefn.cpp, find.cpp,
+match.cpp, os.hpp, pathfn.cpp, rdwrfn.cpp, secpassword.cpp, system.cpp,
+volume.cpp, win32acl.cpp, win32lnk.cpp
+
+For building, premake is used to generate Visual Studio project files.
+See ../build/premake/ for details.
diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/acknow.txt b/Src/external_dependencies/openmpt-trunk/include/unrar/acknow.txt new file mode 100644 index 00000000..ec2c2c7c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/acknow.txt @@ -0,0 +1,59 @@ + ACKNOWLEDGMENTS
+
+* We used "Screaming Fast Galois Field Arithmetic Using Intel
+ SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
+ and Ethan L. Miller to improve Reed-Solomon coding performance.
+ Also we are grateful to Artem Drobanov and Bulat Ziganshin
+ for samples and ideas allowed to make Reed-Solomon coding
+ more efficient.
+
+* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
+ and Dmitry Subbotin carryless rangecoder public domain source code.
+ You can find it in ftp.elf.stuba.sk/pub/pc/pack.
+
+* RAR encryption includes parts of public domain code
+ from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
+
+* With exception of SFX modules, RAR uses CRC32 function based
+ on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
+ is available here:
+
+ https://sourceforge.net/projects/slicing-by-8/
+
+ Original Intel Slicing-by-8 code is licensed under BSD License
+ available at http://www.opensource.org/licenses/bsd-license.html
+
+ Copyright (c) 2004-2006 Intel Corporation.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with
+ the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
+ designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
+ and Christian Winnerlein.
+
+* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
+ to significantly improve RAR compression and speed.
diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/arccmt.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/arccmt.cpp new file mode 100644 index 00000000..8b7e498f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/arccmt.cpp @@ -0,0 +1,185 @@ +static bool IsAnsiEscComment(const wchar *Data,size_t Size); + +bool Archive::GetComment(Array<wchar> *CmtData) +{ + if (!MainComment) + return false; + int64 SavePos=Tell(); + bool Success=DoGetComment(CmtData); + Seek(SavePos,SEEK_SET); + return Success; +} + + +bool Archive::DoGetComment(Array<wchar> *CmtData) +{ +#ifndef SFX_MODULE + uint CmtLength; + if (Format==RARFMT14) + { + Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET); + CmtLength=GetByte(); + CmtLength+=(GetByte()<<8); + } + else +#endif + { + if (MainHead.CommentInHeader) + { + // Old style (RAR 2.9) archive comment embedded into the main + // archive header. + Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET); + if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT) + return false; + } + else + { + // Current (RAR 3.0+) version of archive comment. + Seek(GetStartPos(),SEEK_SET); + return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData); + } +#ifndef SFX_MODULE + // Old style (RAR 2.9) comment header embedded into the main + // archive header. + if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD; +#endif + } +#ifndef SFX_MODULE + if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30) + { + if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35)) + return false; + ComprDataIO DataIO; + DataIO.SetTestMode(true); + uint UnpCmtLength; + if (Format==RARFMT14) + { +#ifdef RAR_NOCRYPT + return false; +#else + UnpCmtLength=GetByte(); + UnpCmtLength+=(GetByte()<<8); + if (CmtLength<2) + return false; + CmtLength-=2; + DataIO.SetCmt13Encryption(); + CommHead.UnpVer=15; +#endif + } + else + UnpCmtLength=CommHead.UnpSize; + DataIO.SetFiles(this,NULL); + DataIO.EnableShowProgress(false); + DataIO.SetPackedSizeToRead(CmtLength); + DataIO.UnpHash.Init(HASH_CRC32,1); + DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet. + + Unpack CmtUnpack(&DataIO); + CmtUnpack.Init(0x10000,false); + CmtUnpack.SetDestSize(UnpCmtLength); + CmtUnpack.DoUnpack(CommHead.UnpVer,false); + + if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + else + { + byte *UnpData; + size_t UnpDataSize; + DataIO.GetUnpackedData(&UnpData,&UnpDataSize); + if (UnpDataSize>0) + { +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); +#endif + CmtData->Alloc(UnpDataSize+1); + memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); + CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } + } + } + else + { + if (CmtLength==0) + return false; + Array<byte> CmtRaw(CmtLength); + int ReadSize=Read(&CmtRaw[0],CmtLength); + if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared. + { + CmtLength=ReadSize; + CmtRaw.Alloc(CmtLength); + } + + if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff)) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + CmtData->Alloc(CmtLength+1); + CmtRaw.Push(0); +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); +#endif + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } +#endif + return CmtData->Size() > 0; +} + + +bool Archive::ReadCommentData(Array<wchar> *CmtData) +{ + Array<byte> CmtRaw; + if (!ReadSubData(&CmtRaw,NULL,false)) + return false; + size_t CmtSize=CmtRaw.Size(); + CmtRaw.Push(0); + CmtData->Alloc(CmtSize+1); + if (Format==RARFMT50) + UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + else + if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) + { + RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); + (*CmtData)[CmtSize/2]=0; + + } + else + { + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + } + CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. + return true; +} + + +void Archive::ViewComment() +{ + if (Cmd->DisableComment) + return; + Array<wchar> CmtBuf; + if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. + { + size_t CmtSize=CmtBuf.Size(); + wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); + if (ChPtr!=NULL) + CmtSize=ChPtr-&CmtBuf[0]; + mprintf(L"\n"); + OutComment(&CmtBuf[0],CmtSize); + } +} + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/archive.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/archive.cpp new file mode 100644 index 00000000..0f5de946 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/archive.cpp @@ -0,0 +1,338 @@ +#include "rar.hpp" + +#include "arccmt.cpp" + + +Archive::Archive(RAROptions *InitCmd) +{ + Cmd=NULL; // Just in case we'll have an exception in 'new' below. + + DummyCmd=(InitCmd==NULL); + Cmd=DummyCmd ? (new RAROptions):InitCmd; + + OpenShared=Cmd->OpenShared; + Format=RARFMT15; + Solid=false; + Volume=false; + MainComment=false; + Locked=false; + Signed=false; + FirstVolume=false; + NewNumbering=false; + SFXSize=0; + LatestTime.Reset(); + Protected=false; + Encrypted=false; + FailedHeaderDecryption=false; + BrokenHeader=false; + LastReadBlock=0; + + CurBlockPos=0; + NextBlockPos=0; + + + memset(&MainHead,0,sizeof(MainHead)); + memset(&CryptHead,0,sizeof(CryptHead)); + memset(&EndArcHead,0,sizeof(EndArcHead)); + + VolNumber=0; + VolWrite=0; + AddingFilesSize=0; + AddingHeadersSize=0; + *FirstVolumeName=0; + + Splitting=false; + NewArchive=false; + + SilentOpen=false; + +#ifdef USE_QOPEN + ProhibitQOpen=false; +#endif + +} + + +Archive::~Archive() +{ + if (DummyCmd) + delete Cmd; +} + + +void Archive::CheckArc(bool EnableBroken) +{ + if (!IsArchive(EnableBroken)) + { + // If FailedHeaderDecryption is set, we already reported that archive + // password is incorrect. + if (!FailedHeaderDecryption) + uiMsg(UIERROR_BADARCHIVE,FileName); + ErrHandler.Exit(RARX_FATAL); + } +} + + +#if !defined(SFX_MODULE) +void Archive::CheckOpen(const wchar *Name) +{ + TOpen(Name); + CheckArc(false); +} +#endif + + +bool Archive::WCheckOpen(const wchar *Name) +{ + if (!WOpen(Name)) + return false; + if (!IsArchive(false)) + { + uiMsg(UIERROR_BADARCHIVE,FileName); + Close(); + return false; + } + return true; +} + + +RARFORMAT Archive::IsSignature(const byte *D,size_t Size) +{ + RARFORMAT Type=RARFMT_NONE; + if (Size>=1 && D[0]==0x52) +#ifndef SFX_MODULE + if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + Type=RARFMT14; + else +#endif + if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) + { + // We check the last signature byte, so we can return a sensible + // warning in case we'll want to change the archive format + // sometimes in the future. + if (D[6]==0) + Type=RARFMT15; + else + if (D[6]==1) + Type=RARFMT50; + else + if (D[6]>1 && D[6]<5) + Type=RARFMT_FUTURE; + } + return Type; +} + + +bool Archive::IsArchive(bool EnableBroken) +{ + Encrypted=false; + BrokenHeader=false; // Might be left from previous volume. + +#ifndef SFX_MODULE + if (IsDevice()) + { + uiMsg(UIERROR_INVALIDNAME,FileName,FileName); + return false; + } +#endif + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) + return false; + SFXSize=0; + + RARFORMAT Type; + if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) + { + Format=Type; + if (Format==RARFMT14) + Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); + } + else + { + Array<char> Buffer(MAXSFXSIZE); + long CurPos=(long)Tell(); + int ReadSize=Read(&Buffer[0],Buffer.Size()-16); + for (int I=0;I<ReadSize;I++) + if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE) + { + Format=Type; + if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31) + { + char *D=&Buffer[28-CurPos]; + if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) + continue; + } + SFXSize=CurPos+I; + Seek(SFXSize,SEEK_SET); + if (Format==RARFMT15 || Format==RARFMT50) + Read(MarkHead.Mark,SIZEOF_MARKHEAD3); + break; + } + if (SFXSize==0) + return false; + } + if (Format==RARFMT_FUTURE) + { + uiMsg(UIERROR_NEWRARFORMAT,FileName); + return false; + } + if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. + { + if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) + return false; + MarkHead.HeadSize=SIZEOF_MARKHEAD5; + } + else + MarkHead.HeadSize=SIZEOF_MARKHEAD3; + +#ifdef RARDLL + // If callback function is not set, we cannot get the password, + // so we skip the initial header processing for encrypted header archive. + // It leads to skipped archive comment, but the rest of archive data + // is processed correctly. + if (Cmd->Callback==NULL) + SilentOpen=true; +#endif + + bool HeadersLeft; // Any headers left to read. + bool StartFound=false; // Main or encryption headers found. + // Skip the archive encryption header if any and read the main header. + while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang. + { + SeekToNext(); + + HEADER_TYPE Type=GetHeaderType(); + // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to + // avoid the password prompt. + StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT; + if (StartFound) + break; + } + + + // We should not do it for EnableBroken or we'll get 'not RAR archive' + // messages when extracting encrypted archives with wrong password. + if (FailedHeaderDecryption && !EnableBroken) + return false; + + if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing. + { + if (!FailedHeaderDecryption) // If not reported a wrong password already. + uiMsg(UIERROR_MHEADERBROKEN,FileName); + if (!EnableBroken) + return false; + } + + MainComment=MainHead.CommentInHeader; + + // If we process non-encrypted archive or can request a password, + // we set 'first volume' flag based on file attributes below. + // It is necessary for RAR 2.x archives, which did not have 'first volume' + // flag in main header. Also for all RAR formats we need to scan until + // first file header to set "comment" flag when reading service header. + // Unless we are in silent mode, we need to know about presence of comment + // immediately after IsArchive call. + if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable()) + { + int64 SavePos=Tell(); + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + while (ReadHeader()!=0) + { + HEADER_TYPE HeaderType=GetHeaderType(); + if (HeaderType==HEAD_SERVICE) + { + // If we have a split service headers, it surely indicates non-first + // volume. But not split service header does not guarantee the first + // volume, because we can have split file after non-split archive + // comment. So we do not quit from loop here. + FirstVolume=Volume && !SubHead.SplitBefore; + } + else + if (HeaderType==HEAD_FILE) + { + FirstVolume=Volume && !FileHead.SplitBefore; + break; + } + else + if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. + break; + SeekToNext(); + } + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + Seek(SavePos,SEEK_SET); + } + if (!Volume || FirstVolume) + wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName)); + + return true; +} + + + + +void Archive::SeekToNext() +{ + Seek(NextBlockPos,SEEK_SET); +} + + + + + + +// Calculate the block size including encryption fields and padding if any. +uint Archive::FullHeaderSize(size_t Size) +{ + if (Encrypted) + { + Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. + if (Format == RARFMT50) + Size += SIZE_INITV; + else + Size += SIZE_SALT30; + } + return uint(Size); +} + + + + +#ifdef USE_QOPEN +bool Archive::Open(const wchar *Name,uint Mode) +{ + // Important if we reuse Archive object and it has virtual QOpen + // file position not matching real. For example, for 'l -v volname'. + QOpen.Unload(); + + return File::Open(Name,Mode); +} + + +int Archive::Read(void *Data,size_t Size) +{ + size_t Result; + if (QOpen.Read(Data,Size,Result)) + return (int)Result; + return File::Read(Data,Size); +} + + +void Archive::Seek(int64 Offset,int Method) +{ + if (!QOpen.Seek(Offset,Method)) + File::Seek(Offset,Method); +} + + +int64 Archive::Tell() +{ + int64 QPos; + if (QOpen.Tell(&QPos)) + return QPos; + return File::Tell(); +} +#endif + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/archive.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/archive.hpp new file mode 100644 index 00000000..d9518f1d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/archive.hpp @@ -0,0 +1,148 @@ +#ifndef _RAR_ARCHIVE_ +#define _RAR_ARCHIVE_ + +class PPack; +class RawRead; +class RawWrite; + +enum NOMODIFY_FLAGS +{ + NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4 +}; + +enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE}; + +enum ADDSUBDATA_FLAGS +{ + ASDF_SPLIT = 1, // Allow to split archive just before header if necessary. + ASDF_COMPRESS = 2, // Allow to compress data following subheader. + ASDF_CRYPT = 4, // Encrypt data after subheader if password is set. + ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode. +}; + +// RAR5 headers must not exceed 2 MB. +#define MAX_HEADER_SIZE_RAR5 0x200000 + +class Archive:public File +{ + private: + void UpdateLatestTime(FileHeader *CurBlock); + void ConvertNameCase(wchar *Name); + void ConvertFileHeader(FileHeader *hd); + size_t ReadHeader14(); + size_t ReadHeader15(); + size_t ReadHeader50(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); + void RequestArcPassword(); + void UnexpEndArcMsg(); + void BrokenHeaderMsg(); + void UnkEncVerMsg(const wchar *Name,const wchar *Info); + bool DoGetComment(Array<wchar> *CmtData); + bool ReadCommentData(Array<wchar> *CmtData); + +#if !defined(RAR_NOCRYPT) + CryptData HeadersCrypt; +#endif + ComprDataIO SubDataIO; + bool DummyCmd; + RAROptions *Cmd; + + + RarTime LatestTime; + int LastReadBlock; + HEADER_TYPE CurHeaderType; + + bool SilentOpen; +#ifdef USE_QOPEN + QuickOpen QOpen; + bool ProhibitQOpen; +#endif + public: + Archive(RAROptions *InitCmd=NULL); + ~Archive(); + static RARFORMAT IsSignature(const byte *D,size_t Size); + bool IsArchive(bool EnableBroken); + size_t SearchBlock(HEADER_TYPE HeaderType); + size_t SearchSubBlock(const wchar *Type); + size_t SearchRR(); + size_t ReadHeader(); + void CheckArc(bool EnableBroken); + void CheckOpen(const wchar *Name); + bool WCheckOpen(const wchar *Name); + bool GetComment(Array<wchar> *CmtData); + void ViewComment(); + void SetLatestTime(RarTime *NewTime); + void SeekToNext(); + bool CheckAccess(); + bool IsArcDir(); + void ConvertAttributes(); + void VolSubtractHeaderSize(size_t SubSize); + uint FullHeaderSize(size_t Size); + int64 GetStartPos(); + void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, + const wchar *Name,uint Flags); + bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode); + HEADER_TYPE GetHeaderType() {return CurHeaderType;} + RAROptions* GetRAROptions() {return Cmd;} + void SetSilentOpen(bool Mode) {SilentOpen=Mode;} +#if 0 + void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); +#endif +#ifdef USE_QOPEN + bool Open(const wchar *Name,uint Mode=FMF_READ); + int Read(void *Data,size_t Size); + void Seek(int64 Offset,int Method); + int64 Tell(); + void QOpenUnload() {QOpen.Unload();} + void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} +#endif + + BaseBlock ShortBlock; + MarkHeader MarkHead; + MainHeader MainHead; + CryptHeader CryptHead; + FileHeader FileHead; + EndArcHeader EndArcHead; + SubBlockHeader SubBlockHead; + FileHeader SubHead; + CommentHeader CommHead; + ProtectHeader ProtectHead; + UnixOwnersHeader UOHead; + EAHeader EAHead; + StreamHeader StreamHead; + + int64 CurBlockPos; + int64 NextBlockPos; + + RARFORMAT Format; + bool Solid; + bool Volume; + bool MainComment; + bool Locked; + bool Signed; + bool FirstVolume; + bool NewNumbering; + bool Protected; + bool Encrypted; + size_t SFXSize; + bool BrokenHeader; + bool FailedHeaderDecryption; + +#if !defined(RAR_NOCRYPT) + byte ArcSalt[SIZE_SALT50]; +#endif + + bool Splitting; + + uint VolNumber; + int64 VolWrite; + uint64 AddingFilesSize; + uint64 AddingHeadersSize; + + bool NewArchive; + + wchar FirstVolumeName[NM]; +}; + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/arcread.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/arcread.cpp new file mode 100644 index 00000000..1a401f48 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/arcread.cpp @@ -0,0 +1,1497 @@ +#include "rar.hpp" + +size_t Archive::ReadHeader() +{ + // Once we failed to decrypt an encrypted block, there is no reason to + // attempt to do it further. We'll never be successful and only generate + // endless errors. + if (FailedHeaderDecryption) + return 0; + + CurBlockPos=Tell(); + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + size_t ReadSize=0; + + switch(Format) + { +#ifndef SFX_MODULE + case RARFMT14: + ReadSize=ReadHeader14(); + break; +#endif + case RARFMT15: + ReadSize=ReadHeader15(); + break; + case RARFMT50: + ReadSize=ReadHeader50(); + break; + } + + // It is important to check ReadSize>0 here, because it is normal + // for RAR2 and RAR3 archives without end of archive block to have + // NextBlockPos==CurBlockPos after the end of archive has reached. + if (ReadSize>0 && NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + ReadSize=0; + } + + if (ReadSize==0) + CurHeaderType=HEAD_UNKNOWN; + + return ReadSize; +} + + +size_t Archive::SearchBlock(HEADER_TYPE HeaderType) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && + (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HeaderType) + return Size; + SeekToNext(); + } + return 0; +} + + +size_t Archive::SearchSubBlock(const wchar *Type) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) + return Size; + SeekToNext(); + } + return 0; +} + + +size_t Archive::SearchRR() +{ + // If locator extra field is available for recovery record, let's utilize it. + if (MainHead.Locator && MainHead.RROffset!=0) + { + uint64 CurPos=Tell(); + Seek(MainHead.RROffset,SEEK_SET); + size_t Size=ReadHeader(); + if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) + return Size; + Seek(CurPos,SEEK_SET); + } + // Otherwise scan the entire archive to find the recovery record. + return SearchSubBlock(SUBHEAD_TYPE_RR); +} + + +void Archive::UnexpEndArcMsg() +{ + int64 ArcSize=FileLength(); + + // If block positions are equal to file size, this is not an error. + // It can happen when we reached the end of older RAR 1.5 archive, + // which did not have the end of archive block. + if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) + { + uiMsg(UIERROR_UNEXPEOF,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + } +} + + +void Archive::BrokenHeaderMsg() +{ + uiMsg(UIERROR_HEADERBROKEN,FileName); + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); +} + + +void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info) +{ + uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); + ErrHandler.SetErrorCode(RARX_WARNING); +} + + +// Return f in case of signed integer overflow or negative parameters +// or v1+v2 otherwise. We use it for file offsets, which are signed +// for compatibility with off_t in POSIX file functions and third party code. +// Signed integer overflow is the undefined behavior according to +// C++ standard and it causes fuzzers to complain. +inline int64 SafeAdd(int64 v1,int64 v2,int64 f) +{ + return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f; +} + + +size_t Archive::ReadHeader15() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; + + if (Decrypt) + { +#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. + return 0; +#else + RequestArcPassword(); + + byte Salt[SIZE_SALT30]; + if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) + { + UnexpEndArcMsg(); + return 0; + } + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); + Raw.SetCrypt(&HeadersCrypt); +#endif + } + + Raw.Read(SIZEOF_SHORTBLOCKHEAD); + if (Raw.Size()==0) + { + UnexpEndArcMsg(); + return 0; + } + + ShortBlock.HeadCRC=Raw.Get2(); + + ShortBlock.Reset(); + + uint HeaderType=Raw.Get1(); + ShortBlock.Flags=Raw.Get2(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0; + ShortBlock.HeadSize=Raw.Get2(); + + ShortBlock.HeaderType=(HEADER_TYPE)HeaderType; + if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD) + { + BrokenHeaderMsg(); + return 0; + } + + // For simpler further processing we map header types common + // for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include + // header types specific for RAR 1.5 - 4.x only. + switch(ShortBlock.HeaderType) + { + case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break; + case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break; + case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break; + case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break; + } + CurHeaderType=ShortBlock.HeaderType; + + if (ShortBlock.HeaderType==HEAD3_CMT) + { + // Old style (up to RAR 2.9) comment header embedded into main + // or file header. We must not read the entire ShortBlock.HeadSize here + // to not break the comment processing logic later. + Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD); + } + else + if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0) + { + // Old style (up to RAR 2.9) main archive comment embedded into + // the main archive header found. While we can read the entire + // ShortBlock.HeadSize here and remove this part of "if", it would be + // waste of memory, because we'll read and process this comment data + // in other function anyway and we do not need them here now. + Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD); + } + else + Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD); + + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); + + switch(ShortBlock.HeaderType) + { + case HEAD_MAIN: + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + MainHead.HighPosAV=Raw.Get2(); + MainHead.PosAV=Raw.Get4(); + + Volume=(MainHead.Flags & MHD_VOLUME)!=0; + Solid=(MainHead.Flags & MHD_SOLID)!=0; + Locked=(MainHead.Flags & MHD_LOCK)!=0; + Protected=(MainHead.Flags & MHD_PROTECT)!=0; + Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0; + Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0; + MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0; + + // Only for encrypted 3.0+ archives. 2.x archives did not have this + // flag, so for non-encrypted archives, we'll set it later based on + // file attributes. + FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0; + + NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0; + break; + case HEAD_FILE: + case HEAD_SERVICE: + { + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + FileHeader *hd=FileBlock ? &FileHead:&SubHead; + hd->Reset(); + + *(BaseBlock *)hd=ShortBlock; + + hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; + hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; + hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; + hd->SaltSet=(hd->Flags & LHD_SALT)!=0; + hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; + hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); + hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; + hd->Version=(hd->Flags & LHD_VERSION)!=0; + + hd->DataSize=Raw.Get4(); + uint LowUnpSize=Raw.Get4(); + hd->HostOS=Raw.Get1(); + + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + + uint FileTime=Raw.Get4(); + hd->UnpVer=Raw.Get1(); + + hd->Method=Raw.Get1()-0x30; + size_t NameSize=Raw.Get2(); + hd->FileAttr=Raw.Get4(); + + // RAR15 did not use the special dictionary size to mark dirs. + if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) + hd->Dir=true; + + hd->CryptMethod=CRYPT_NONE; + if (hd->Encrypted) + switch(hd->UnpVer) + { + case 13: hd->CryptMethod=CRYPT_RAR13; break; + case 15: hd->CryptMethod=CRYPT_RAR15; break; + case 20: + case 26: hd->CryptMethod=CRYPT_RAR20; break; + default: hd->CryptMethod=CRYPT_RAR30; break; + } + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS<HOST_MAX) + hd->HSType=HSYS_WINDOWS; + + hd->RedirType=FSREDIR_NONE; + + // RAR 4.x Unix symlink. + if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) + { + hd->RedirType=FSREDIR_UNIXSYMLINK; + *hd->RedirName=0; + } + + hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; + + hd->LargeFile=(hd->Flags & LHD_LARGE)!=0; + + uint HighPackSize,HighUnpSize; + if (hd->LargeFile) + { + HighPackSize=Raw.Get4(); + HighUnpSize=Raw.Get4(); + hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff); + } + else + { + HighPackSize=HighUnpSize=0; + // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates + // that we do not know the unpacked file size and must unpack it + // until we find the end of file marker in compressed data. + hd->UnknownUnpSize=(LowUnpSize==0xffffffff); + } + hd->PackSize=INT32TO64(HighPackSize,hd->DataSize); + hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize); + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + if (FileBlock) + { + *hd->FileName=0; + if ((hd->Flags & LHD_UNICODE)!=0) + { + EncodeFileName NameCoder; + size_t Length=strlen(FileName); + Length++; + if (ReadNameSize>Length) + NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length, + ReadNameSize-Length,hd->FileName, + ASIZE(hd->FileName)); + } + + if (*hd->FileName==0) + ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); + +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + else + { + CharToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Calculate the size of optional data. + int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); + if ((hd->Flags & LHD_SALT)!=0) + DataSize-=SIZE_SALT30; + + if (DataSize>0) + { + // Here we read optional additional fields for subheaders. + // They are stored after the file name and before salt. + hd->SubData.Alloc(DataSize); + Raw.GetB(&hd->SubData[0],DataSize); + + } + + if (hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + } + if ((hd->Flags & LHD_SALT)!=0) + Raw.GetB(hd->Salt,SIZE_SALT30); + hd->mtime.SetDos(FileTime); + if ((hd->Flags & LHD_EXTTIME)!=0) + { + ushort Flags=Raw.Get2(); + RarTime *tbl[4]; + tbl[0]=&FileHead.mtime; + tbl[1]=&FileHead.ctime; + tbl[2]=&FileHead.atime; + tbl[3]=NULL; // Archive time is not used now. + for (int I=0;I<4;I++) + { + RarTime *CurTime=tbl[I]; + uint rmode=Flags>>(3-I)*4; + if ((rmode & 8)==0 || CurTime==NULL) + continue; + if (I!=0) + { + uint DosTime=Raw.Get4(); + CurTime->SetDos(DosTime); + } + RarLocalTime rlt; + CurTime->GetLocal(&rlt); + if (rmode & 4) + rlt.Second++; + rlt.Reminder=0; + uint count=rmode&3; + for (uint J=0;J<count;J++) + { + byte CurByte=Raw.Get1(); + rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8)); + } + // Convert from 100ns RAR precision to REMINDER_PRECISION. + rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000; + CurTime->SetLocal(&rlt); + } + } + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); + + bool CRCProcessedOnly=hd->CommentInHeader; + ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); + if (hd->HeadCRC!=HeaderCRC) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_WARNING); + + // If we have a broken encrypted header, we do not need to display + // the error message here, because it will be displayed for such + // headers later in this function. Also such headers are unlikely + // to have anything sensible in file name field, so it is useless + // to display the file name. + if (!Decrypt) + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + } + break; + case HEAD_ENDARC: + *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; + EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; + EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; + EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0; + if (EndArcHead.DataCRC) + EndArcHead.ArcDataCRC=Raw.Get4(); + if (EndArcHead.StoreVolNumber) + VolNumber=EndArcHead.VolNumber=Raw.Get2(); + break; +#ifndef SFX_MODULE + case HEAD3_CMT: + *(BaseBlock *)&CommHead=ShortBlock; + CommHead.UnpSize=Raw.Get2(); + CommHead.UnpVer=Raw.Get1(); + CommHead.Method=Raw.Get1(); + CommHead.CommCRC=Raw.Get2(); + break; + case HEAD3_PROTECT: + *(BaseBlock *)&ProtectHead=ShortBlock; + ProtectHead.DataSize=Raw.Get4(); + ProtectHead.Version=Raw.Get1(); + ProtectHead.RecSectors=Raw.Get2(); + ProtectHead.TotalBlocks=Raw.Get4(); + Raw.GetB(ProtectHead.Mark,8); + NextBlockPos+=ProtectHead.DataSize; + break; + case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. + *(BaseBlock *)&SubBlockHead=ShortBlock; + SubBlockHead.DataSize=Raw.Get4(); + NextBlockPos+=SubBlockHead.DataSize; + SubBlockHead.SubType=Raw.Get2(); + SubBlockHead.Level=Raw.Get1(); + switch(SubBlockHead.SubType) + { + case UO_HEAD: + *(SubBlockHeader *)&UOHead=SubBlockHead; + UOHead.OwnerNameSize=Raw.Get2(); + UOHead.GroupNameSize=Raw.Get2(); + if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName)) + UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1; + if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName)) + UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1; + Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize); + Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize); + UOHead.OwnerName[UOHead.OwnerNameSize]=0; + UOHead.GroupName[UOHead.GroupNameSize]=0; + break; + case NTACL_HEAD: + *(SubBlockHeader *)&EAHead=SubBlockHead; + EAHead.UnpSize=Raw.Get4(); + EAHead.UnpVer=Raw.Get1(); + EAHead.Method=Raw.Get1(); + EAHead.EACRC=Raw.Get4(); + break; + case STREAM_HEAD: + *(SubBlockHeader *)&StreamHead=SubBlockHead; + StreamHead.UnpSize=Raw.Get4(); + StreamHead.UnpVer=Raw.Get1(); + StreamHead.Method=Raw.Get1(); + StreamHead.StreamCRC=Raw.Get4(); + StreamHead.StreamNameSize=Raw.Get2(); + if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) + StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; + Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); + StreamHead.StreamName[StreamHead.StreamNameSize]=0; + break; + } + break; +#endif + default: + if (ShortBlock.Flags & LONG_BLOCK) + NextBlockPos+=Raw.Get4(); + break; + } + + ushort HeaderCRC=Raw.GetCRC15(false); + + // Old AV header does not have header CRC properly set. + if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && + ShortBlock.HeaderType!=HEAD3_AV) + { + bool Recovered=false; + if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) + { + // Last 7 bytes of recovered volume can contain zeroes, because + // REV files store its own information (volume number, etc.) here. + int64 Length=Tell(); + Seek(Length-7,SEEK_SET); + Recovered=true; + for (int J=0;J<7;J++) + if (GetByte()!=0) + Recovered=false; + } + if (!Recovered) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + + if (Decrypt) + { + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; + } + } + } + + return Raw.Size(); +} + + +size_t Archive::ReadHeader50() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; + + if (Decrypt) + { +#if defined(RAR_NOCRYPT) + return 0; +#else + + if (Cmd->SkipEncrypted) + { + uiMsg(UIMSG_SKIPENCARC,FileName); + FailedHeaderDecryption=true; // Suppress error messages and quit quietly. + return 0; + } + + byte HeadersInitV[SIZE_INITV]; + if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) + { + UnexpEndArcMsg(); + return 0; + } + + // We repeat the password request only for manually entered passwords + // and not for -p<pwd>. Wrong password can be intentionally provided + // in -p<pwd> to not stop batch processing for encrypted archives. + bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); + + while (true) // Repeat the password prompt for wrong passwords. + { + RequestArcPassword(); + + byte PswCheck[SIZE_PSWCHECK]; + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); + // Verify password validity. + if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) + { + if (GlobalPassword) // For -p<pwd> or Ctrl+P. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,FileName,FileName); + FailedHeaderDecryption=true; + ErrHandler.SetErrorCode(RARX_BADPWD); + return 0; + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,FileName,FileName); + Cmd->Password.Clean(); + } + +#ifdef RARDLL + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. + ErrHandler.SetErrorCode(RARX_BADPWD); + Cmd->DllError=ERAR_BAD_PASSWORD; + ErrHandler.Exit(RARX_BADPWD); +#else + continue; // Request a password again. +#endif + } + break; + } + + Raw.SetCrypt(&HeadersCrypt); +#endif + } + + // Header size must not occupy more than 3 variable length integer bytes + // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5), + // so here we read 4 byte CRC32 followed by 3 bytes or less of header size. + const size_t FirstReadSize=7; // Smallest possible block size. + if (Raw.Read(FirstReadSize)<FirstReadSize) + { + UnexpEndArcMsg(); + return 0; + } + + ShortBlock.Reset(); + ShortBlock.HeadCRC=Raw.Get4(); + uint SizeBytes=Raw.GetVSize(4); + uint64 BlockSize=Raw.GetV(); + + if (BlockSize==0 || SizeBytes==0) + { + BrokenHeaderMsg(); + return 0; + } + + int SizeToRead=int(BlockSize); + SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. + uint HeaderSize=4+SizeBytes+(uint)BlockSize; + + if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5) + { + BrokenHeaderMsg(); + return 0; + } + + Raw.Read(SizeToRead); + + if (Raw.Size()<HeaderSize) + { + UnexpEndArcMsg(); + return 0; + } + + uint HeaderCRC=Raw.GetCRC50(); + + ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV(); + ShortBlock.Flags=(uint)Raw.GetV(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0; + ShortBlock.HeadSize=HeaderSize; + + CurHeaderType=ShortBlock.HeaderType; + + bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC); + if (BadCRC) + { + BrokenHeaderMsg(); // Report, but attempt to process. + + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + + if (Decrypt) + { + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; + } + } + + uint64 ExtraSize=0; + if ((ShortBlock.Flags & HFL_EXTRA)!=0) + { + ExtraSize=Raw.GetV(); + if (ExtraSize>=ShortBlock.HeadSize) + { + BrokenHeaderMsg(); + return 0; + } + } + + uint64 DataSize=0; + if ((ShortBlock.Flags & HFL_DATA)!=0) + DataSize=Raw.GetV(); + + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,DataSize,0); + + switch(ShortBlock.HeaderType) + { + case HEAD_CRYPT: + { + *(BaseBlock *)&CryptHead=ShortBlock; + uint CryptVersion=(uint)Raw.GetV(); + if (CryptVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"h%u",CryptVersion); + UnkEncVerMsg(FileName,Info); + return 0; + } + uint EncFlags=(uint)Raw.GetV(); + CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; + CryptHead.Lg2Count=Raw.Get1(); + if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count); + UnkEncVerMsg(FileName,Info); + return 0; + } + + Raw.GetB(CryptHead.Salt,SIZE_SALT50); + if (CryptHead.UsePswCheck) + { + Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); + + byte csum[SIZE_PSWCHECK_CSUM]; + Raw.GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + } + Encrypted=true; + } + break; + case HEAD_MAIN: + { + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + + Volume=(ArcFlags & MHFL_VOLUME)!=0; + Solid=(ArcFlags & MHFL_SOLID)!=0; + Locked=(ArcFlags & MHFL_LOCK)!=0; + Protected=(ArcFlags & MHFL_PROTECT)!=0; + Signed=false; + NewNumbering=true; + + if ((ArcFlags & MHFL_VOLNUMBER)!=0) + VolNumber=(uint)Raw.GetV(); + else + VolNumber=0; + FirstVolume=Volume && VolNumber==0; + + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); + +#ifdef USE_QOPEN + if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) + { + // We seek to QO block in the end of archive when processing + // QOpen.Load, so we need to preserve current block positions + // to not break normal archive processing by calling function. + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + QOpen.Init(this,false); + QOpen.Load(MainHead.QOpenOffset); + + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + } +#endif + } + break; + case HEAD_FILE: + case HEAD_SERVICE: + { + FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; + hd->Reset(); // Clear hash, time fields and other stuff like flags. + *(BaseBlock *)hd=ShortBlock; + + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + + hd->LargeFile=true; + + hd->PackSize=DataSize; + hd->FileFlags=(uint)Raw.GetV(); + hd->UnpSize=Raw.GetV(); + + hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0; + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + hd->MaxSize=Max(hd->PackSize,hd->UnpSize); + hd->FileAttr=(uint)Raw.GetV(); + if ((hd->FileFlags & FHFL_UTIME)!=0) + hd->mtime.SetUnix((time_t)Raw.Get4()); + + hd->FileHash.Type=HASH_NONE; + if ((hd->FileFlags & FHFL_CRC32)!=0) + { + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + } + + hd->RedirType=FSREDIR_NONE; + + uint CompInfo=(uint)Raw.GetV(); + hd->Method=(CompInfo>>7) & 7; + + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + hd->UnpVer=(CompInfo & 0x3f) + 50; + if (hd->UnpVer!=50) // Only 5.0 compression is known now. + hd->UnpVer=VER_UNKNOWN; + + hd->HostOS=(byte)Raw.GetV(); + size_t NameSize=(size_t)Raw.GetV(); + hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0; + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST5_UNIX) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS==HOST5_WINDOWS) + hd->HSType=HSYS_WINDOWS; + + hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0; + hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0; + hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; + hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; + hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; + hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); + + hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Should do it before converting names, because extra fields can + // affect name processing, like in case of NTFS streams. + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,hd); + + if (FileBlock) + { +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + +#if 0 + // For RAR5 format we read the user specified recovery percent here. + // It would be useful to do it for shell extension too, so we display + // the correct recovery record size in archive properties. But then + // we would need to include the entire recovery record processing + // code to shell extension, which is not done now. + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) + { + // It is stored as a single byte up to RAR 6.02 and as vint since + // 6.10, where we extended the maximum RR size from 99% to 1000%. + RawRead RawPercent; + RawPercent.Read(&hd->SubData[0],hd->SubData.Size()); + RecoveryPercent=(int)RawPercent.GetV(); + + RSBlockHeader Header; + GetRRInfo(this,&Header); + RecoverySize=Header.RecSectionSize*Header.RecCount; + } +#endif + + if (BadCRC) // Add the file name to broken header message displayed above. + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + break; + case HEAD_ENDARC: + { + *(BaseBlock *)&EndArcHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; + EndArcHead.StoreVolNumber=false; + EndArcHead.DataCRC=false; + EndArcHead.RevSpace=false; + } + break; + } + + return Raw.Size(); +} + + +#if !defined(RAR_NOCRYPT) +void Archive::RequestArcPassword() +{ + if (!Cmd->Password.IsSet()) + { +#ifdef RARDLL + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + { + Close(); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ErrHandler.Exit(RARX_USERBREAK); + } +#else + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password)) + { + Close(); + uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. + ErrHandler.Exit(RARX_USERBREAK); + } +#endif + Cmd->ManualPassword=true; + } +} +#endif + + +void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) +{ + // Read extra data from the end of block skipping any fields before it. + size_t ExtraStart=Raw->Size()-ExtraSize; + if (ExtraStart<Raw->GetPos()) + return; + Raw->SetPos(ExtraStart); + while (Raw->DataLeft()>=2) + { + int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative. + if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) + break; + size_t NextPos=size_t(Raw->GetPos()+FieldSize); + uint64 FieldType=Raw->GetV(); + + FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. + + if (FieldSize<0) // FieldType is longer than expected extra field size. + break; + + if (bb->HeaderType==HEAD_MAIN) + { + MainHeader *hd=(MainHeader *)bb; + if (FieldType==MHEXTRA_LOCATOR) + { + hd->Locator=true; + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->QOpenOffset=Offset+CurBlockPos; + } + if ((Flags & MHEXTRA_LOCATOR_RR)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->RROffset=Offset+CurBlockPos; + } + } + } + + if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) + { + FileHeader *hd=(FileHeader *)bb; + switch(FieldType) + { + case FHEXTRA_CRYPT: + { + FileHeader *hd=(FileHeader *)bb; + uint EncVersion=(uint)Raw->GetV(); + if (EncVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"x%u",EncVersion); + UnkEncVerMsg(hd->FileName,Info); + } + else + { + uint Flags=(uint)Raw->GetV(); + hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; + hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; + hd->Lg2Count=Raw->Get1(); + if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count); + UnkEncVerMsg(hd->FileName,Info); + } + Raw->GetB(hd->Salt,SIZE_SALT50); + Raw->GetB(hd->InitV,SIZE_INITV); + if (hd->UsePswCheck) + { + Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); + + // It is important to know if password check data is valid. + // If it is damaged and header CRC32 fails to detect it, + // archiver would refuse to decompress a possibly valid file. + // Since we want to be sure distinguishing a wrong password + // or corrupt file data, we use 64-bit password check data + // and to control its validity we use 32 bits of password + // check data SHA-256 additionally to 32-bit header CRC32. + byte csum[SIZE_PSWCHECK_CSUM]; + Raw->GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + + // RAR 5.21 and earlier set PswCheck field in service records to 0 + // even if UsePswCheck was present. + if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) + hd->UsePswCheck=0; + } + hd->SaltSet=true; + hd->CryptMethod=CRYPT_RAR50; + hd->Encrypted=true; + } + } + break; + case FHEXTRA_HASH: + { + FileHeader *hd=(FileHeader *)bb; + uint Type=(uint)Raw->GetV(); + if (Type==FHEXTRA_HASH_BLAKE2) + { + hd->FileHash.Type=HASH_BLAKE2; + Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + } + } + break; + case FHEXTRA_HTIME: + if (FieldSize>=5) + { + byte Flags=(byte)Raw->GetV(); + bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0) + if (UnixTime) + hd->mtime.SetUnix(Raw->Get4()); + else + hd->mtime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0) + if (UnixTime) + hd->ctime.SetUnix(Raw->Get4()); + else + hd->ctime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0) + if (UnixTime) + hd->atime.SetUnix((time_t)Raw->Get4()); + else + hd->atime.SetWin(Raw->Get8()); + if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. + { + uint ns; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->mtime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->ctime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->atime.Adjust(ns); + } + } + break; + case FHEXTRA_VERSION: + if (FieldSize>=1) + { + Raw->GetV(); // Skip flags field. + uint Version=(uint)Raw->GetV(); + if (Version!=0) + { + hd->Version=true; + + wchar VerText[20]; + swprintf(VerText,ASIZE(VerText),L";%u",Version); + wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); + } + } + break; + case FHEXTRA_REDIR: + { + hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); + uint Flags=(uint)Raw->GetV(); + hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; + size_t NameSize=(size_t)Raw->GetV(); + + char UtfName[NM*4]; + *UtfName=0; + if (NameSize<ASIZE(UtfName)-1) + { + Raw->GetB(UtfName,NameSize); + UtfName[NameSize]=0; + } +#ifdef _WIN_ALL + UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName)); +#endif + UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName)); + } + break; + case FHEXTRA_UOWNER: + { + uint Flags=(uint)Raw->GetV(); + hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0; + hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0; + *hd->UnixOwnerName=*hd->UnixGroupName=0; + if ((Flags & FHEXTRA_UOWNER_UNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixOwnerName)-1); + Raw->GetB(hd->UnixOwnerName,Length); + hd->UnixOwnerName[Length]=0; + } + if ((Flags & FHEXTRA_UOWNER_GNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixGroupName)-1); + Raw->GetB(hd->UnixGroupName,Length); + hd->UnixGroupName[Length]=0; + } +#ifdef _UNIX + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uid_t)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(gid_t)Raw->GetV(); +#else + // Need these fields in Windows too for 'list' command, + // but uid_t and gid_t are not defined. + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uint)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(uint)Raw->GetV(); +#endif + hd->UnixOwnerSet=true; + } + break; + case FHEXTRA_SUBDATA: + { + // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than + // required. It did not hurt extraction, because UnRAR 5.21 + // and earlier ignored this field and set FieldSize as data left + // in entire extra area. But now we set the correct field size + // and set FieldSize based on the actual extra record size, + // so we need to adjust it for those older archives here. + // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE + // and always is last in extra area. So since its size is by 1 + // less than needed, we always have 1 byte left in extra area, + // which fact we use here to detect such archives. + if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) + FieldSize++; + + // We cannot allocate too much memory here, because above + // we check FieldSize againt Raw size and we control that Raw size + // is sensible when reading headers. + hd->SubData.Alloc((size_t)FieldSize); + Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); + } + break; + } + } + + Raw->SetPos(NextPos); + } +} + + +#ifndef SFX_MODULE +size_t Archive::ReadHeader14() +{ + RawRead Raw(this); + if (CurBlockPos<=(int64)SFXSize) + { + Raw.Read(SIZEOF_MAINHEAD14); + MainHead.Reset(); + byte Mark[4]; + Raw.GetB(Mark,4); + uint HeadSize=Raw.Get2(); + if (HeadSize<7) + return false; + byte Flags=Raw.Get1(); + NextBlockPos=CurBlockPos+HeadSize; + CurHeaderType=HEAD_MAIN; + + Volume=(Flags & MHD_VOLUME)!=0; + Solid=(Flags & MHD_SOLID)!=0; + Locked=(Flags & MHD_LOCK)!=0; + MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0; + MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0; + } + else + { + Raw.Read(SIZEOF_FILEHEAD14); + FileHead.Reset(); + + FileHead.HeaderType=HEAD_FILE; + FileHead.DataSize=Raw.Get4(); + FileHead.UnpSize=Raw.Get4(); + FileHead.FileHash.Type=HASH_RAR14; + FileHead.FileHash.CRC32=Raw.Get2(); + FileHead.HeadSize=Raw.Get2(); + if (FileHead.HeadSize<21) + return false; + uint FileTime=Raw.Get4(); + FileHead.FileAttr=Raw.Get1(); + FileHead.Flags=Raw.Get1()|LONG_BLOCK; + FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10; + size_t NameSize=Raw.Get1(); + FileHead.Method=Raw.Get1(); + + FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0; + FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0; + FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0; + FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; + + FileHead.PackSize=FileHead.DataSize; + FileHead.WinSize=0x10000; + FileHead.Dir=(FileHead.FileAttr & 0x10)!=0; + + FileHead.HostOS=HOST_MSDOS; + FileHead.HSType=HSYS_WINDOWS; + + FileHead.mtime.SetDos(FileTime); + + Raw.Read(NameSize); + + char FileName[NM]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + IntToExt(FileName,FileName,ASIZE(FileName)); + CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); + ConvertNameCase(FileHead.FileName); + ConvertFileHeader(&FileHead); + + if (Raw.Size()!=0) + NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; + CurHeaderType=HEAD_FILE; + } + return NextBlockPos>CurBlockPos ? Raw.Size() : 0; +} +#endif + + +#ifndef SFX_MODULE +void Archive::ConvertNameCase(wchar *Name) +{ + if (Cmd->ConvertNames==NAMES_UPPERCASE) + wcsupper(Name); + if (Cmd->ConvertNames==NAMES_LOWERCASE) + wcslower(Name); +} +#endif + + +bool Archive::IsArcDir() +{ + return FileHead.Dir; +} + + +void Archive::ConvertAttributes() +{ +#if defined(_WIN_ALL) || defined(_EMX) + if (FileHead.HSType!=HSYS_WINDOWS) + FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; +#endif +#ifdef _UNIX + // umask defines which permission bits must not be set by default + // when creating a file or directory. The typical default value + // for the process umask is S_IWGRP | S_IWOTH (octal 022), + // resulting in 0644 mode for new files. + // Normally umask is applied automatically when creating a file, + // but we set attributes with chmod later, so we need to calculate + // resulting attributes here. We do it only for non-Unix archives. + // We restore native Unix attributes as is, because it can be backup. + static mode_t mask = (mode_t) -1; + + if (mask == (mode_t) -1) + { + // umask call returns the current umask value. Argument (022) is not + // really important here. + mask = umask(022); + + // Restore the original umask value, which was changed to 022 above. + umask(mask); + } + + switch(FileHead.HSType) + { + case HSYS_WINDOWS: + { + // Mapping MSDOS, OS/2 and Windows file attributes to Unix. + + if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY + { + // For directories we use 0777 mask. + FileHead.FileAttr=0777 & ~mask; + } + else + if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY + { + // For read only files we use 0444 mask with 'w' bits turned off. + FileHead.FileAttr=0444 & ~mask; + } + else + { + // umask does not set +x for regular files, so we use 0666 + // instead of 0777 as for directories. + FileHead.FileAttr=0666 & ~mask; + } + } + break; + case HSYS_UNIX: + break; + default: + if (FileHead.Dir) + FileHead.FileAttr=0x41ff & ~mask; + else + FileHead.FileAttr=0x81b6 & ~mask; + break; + } +#endif +} + + +void Archive::ConvertFileHeader(FileHeader *hd) +{ + if (hd->HSType==HSYS_UNKNOWN) + if (hd->Dir) + hd->FileAttr=0x10; + else + hd->FileAttr=0x20; + +#ifdef _WIN_ALL + if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. + ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); +#endif + + for (wchar *s=hd->FileName;*s!=0;s++) + { +#ifdef _UNIX + // Backslash is the invalid character for Windows file headers, + // but it can present in Unix file names extracted in Unix. + if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) + *s='_'; +#endif + +#if defined(_WIN_ALL) || defined(_EMX) + // RAR 5.0 archives do not use '\' as path separator, so if we see it, + // it means that it is a part of Unix file name, which we cannot + // extract in Windows. + if (*s=='\\' && Format==RARFMT50) + *s='_'; + + // ':' in file names is allowed in Unix, but not in Windows. + // Even worse, file data will be written to NTFS stream on NTFS, + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better + // replace ':' now. + if (*s==':') + *s='_'; +#endif + + // This code must be performed only after other path separator checks, + // because it produces backslashes illegal for some of checks above. + // Backslash is allowed in file names in Unix, but not in Windows. + // Still, RAR 4.x uses backslashes as path separator even in Unix. + // Forward slash is not allowed in both systems. In RAR 5.0 we use + // the forward slash as universal path separator. + if (*s=='/' || *s=='\\' && Format!=RARFMT50) + *s=CPATHDIVIDER; + } +} + + +int64 Archive::GetStartPos() +{ + int64 StartPos=SFXSize+MarkHead.HeadSize; + if (Format==RARFMT15) + StartPos+=MainHead.HeadSize; + else // RAR 5.0. + StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); + return StartPos; +} + + +bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode) +{ + if (BrokenHeader) + { + uiMsg(UIERROR_SUBHEADERBROKEN,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return false; + } + if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) + { + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + + if (SubHead.PackSize==0 && !SubHead.SplitAfter) + return true; + + SubDataIO.Init(); + Unpack Unpack(&SubDataIO); + Unpack.Init(SubHead.WinSize,false); + + if (DestFile==NULL) + { + if (SubHead.UnpSize>0x1000000) + { + // So huge allocation must never happen in valid archives. + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + if (UnpData==NULL) + SubDataIO.SetTestMode(true); + else + { + UnpData->Alloc((size_t)SubHead.UnpSize); + SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); + } + } + if (SubHead.Encrypted) + if (Cmd->Password.IsSet()) + SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, + SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, + SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); + else + return false; + SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); + SubDataIO.SetPackedSizeToRead(SubHead.PackSize); + SubDataIO.EnableShowProgress(false); + SubDataIO.SetFiles(this,DestFile); + SubDataIO.SetTestMode(TestMode); + SubDataIO.UnpVolume=SubHead.SplitAfter; + SubDataIO.SetSubHeader(&SubHead,NULL); + Unpack.SetDestSize(SubHead.UnpSize); + if (SubHead.Method==0) + CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); + else + Unpack.DoUnpack(SubHead.UnpVer,false); + + if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) + { + uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); + ErrHandler.SetErrorCode(RARX_CRC); + if (UnpData!=NULL) + UnpData->Reset(); + return false; + } + return true; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/array.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/array.hpp new file mode 100644 index 00000000..20d258d5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/array.hpp @@ -0,0 +1,191 @@ +#ifndef _RAR_ARRAY_ +#define _RAR_ARRAY_ + +extern ErrorHandler ErrHandler; + +template <class T> class Array +{ + private: + T *Buffer; + size_t BufSize; + size_t AllocSize; + size_t MaxSize; + bool Secure; // Clean memory if true. + public: + Array(); + Array(size_t Size); + Array(const Array &Src); // Copy constructor. + ~Array(); + inline void CleanData(); + inline T& operator [](size_t Item) const; + inline T* operator + (size_t Pos); + inline size_t Size(); // Returns the size in items, not in bytes. + void Add(size_t Items); + void Alloc(size_t Items); + void Reset(); + void SoftReset(); + void operator = (Array<T> &Src); + void Push(T Item); + void Append(T *Item,size_t Count); + T* Addr(size_t Item) {return Buffer+Item;} + void SetMaxSize(size_t Size) {MaxSize=Size;} + T* Begin() {return Buffer;} + T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} + void SetSecure() {Secure=true;} +}; + + +template <class T> void Array<T>::CleanData() +{ + Buffer=NULL; + BufSize=0; + AllocSize=0; + MaxSize=0; + Secure=false; +} + + +template <class T> Array<T>::Array() +{ + CleanData(); +} + + +template <class T> Array<T>::Array(size_t Size) +{ + CleanData(); + Add(Size); +} + + +// Copy constructor in case we need to pass an object as value. +template <class T> Array<T>::Array(const Array &Src) +{ + CleanData(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template <class T> Array<T>::~Array() +{ + if (Buffer!=NULL) + { + if (Secure) + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } +} + + +template <class T> inline T& Array<T>::operator [](size_t Item) const +{ + return Buffer[Item]; +} + + +template <class T> inline T* Array<T>::operator +(size_t Pos) +{ + return Buffer+Pos; +} + + +template <class T> inline size_t Array<T>::Size() +{ + return BufSize; +} + + +template <class T> void Array<T>::Add(size_t Items) +{ + BufSize+=Items; + if (BufSize>AllocSize) + { + if (MaxSize!=0 && BufSize>MaxSize) + { + ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); + ErrHandler.MemoryError(); + } + + size_t Suggested=AllocSize+AllocSize/4+32; + size_t NewSize=Max(BufSize,Suggested); + + T *NewBuffer; + if (Secure) + { + NewBuffer=(T *)malloc(NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + if (Buffer!=NULL) + { + memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } + } + else + { + NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + } + Buffer=NewBuffer; + AllocSize=NewSize; + } +} + + +template <class T> void Array<T>::Alloc(size_t Items) +{ + if (Items>AllocSize) + Add(Items-BufSize); + else + BufSize=Items; +} + + +template <class T> void Array<T>::Reset() +{ + if (Buffer!=NULL) + { + free(Buffer); + Buffer=NULL; + } + BufSize=0; + AllocSize=0; +} + + +// Reset buffer size, but preserve already allocated memory if any, +// so we can reuse it without wasting time to allocation. +template <class T> void Array<T>::SoftReset() +{ + BufSize=0; +} + + +template <class T> void Array<T>::operator =(Array<T> &Src) +{ + Reset(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template <class T> void Array<T>::Push(T Item) +{ + Add(1); + (*this)[Size()-1]=Item; +} + + +template <class T> void Array<T>::Append(T *Items,size_t Count) +{ + size_t CurSize=Size(); + Add(Count); + memcpy(Buffer+CurSize,Items,Count*sizeof(T)); +} + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.cpp new file mode 100644 index 00000000..317603da --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.cpp @@ -0,0 +1,183 @@ +// Based on public domain code written in 2012 by Samuel Neves + +#include "rar.hpp" + +#ifdef USE_SSE +#include "blake2s_sse.cpp" +#endif + +static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth); +static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ); +static void blake2s_final( blake2s_state *S, byte *digest ); + +#include "blake2sp.cpp" + +static const uint32 blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const byte blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static inline void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = ~0U; +} + + +/* Some helper functions, not necessarily useful */ +static inline void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = ~0U; +} + + +static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + + +/* init2 xors IV with input parameter block */ +void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth) +{ +#ifdef USE_SSE + if (_SSE_Version>=SSE_SSE2) + blake2s_init_sse(); +#endif + + S->init(); // Clean data. + for( int i = 0; i < 8; ++i ) + S->h[i] = blake2s_IV[i]; + + S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block. + S->h[2] ^= node_offset; + S->h[3] ^= (node_depth<<16)|0x20000000; +} + + +#define G(r,i,m,a,b,c,d) \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); + + +static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + uint32 m[16]; + uint32 v[16]; + + for( size_t i = 0; i < 16; ++i ) + m[i] = RawGet4( block + i * 4 ); + + for( size_t i = 0; i < 8; ++i ) + v[i] = S->h[i]; + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows. + { + G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]); + G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]); + G(r,2,m,v[ 2],v[ 6],v[10],v[14]); + G(r,3,m,v[ 3],v[ 7],v[11],v[15]); + G(r,4,m,v[ 0],v[ 5],v[10],v[15]); + G(r,5,m,v[ 1],v[ 6],v[11],v[12]); + G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]); + G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]); + } + + for( size_t i = 0; i < 8; ++i ) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; +} + + +void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) +{ + while( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; + + if( inlen > fill ) + { + memcpy( S->buf + left, in, fill ); // Fill buffer + S->buflen += fill; + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + +#ifdef USE_SSE +#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. + if (_SSE_Version>=SSE_SSE2) +#else + if (_SSE_Version>=SSE_SSSE3) +#endif + blake2s_compress_sse( S, S->buf ); + else + blake2s_compress( S, S->buf ); // Compress +#else + blake2s_compress( S, S->buf ); // Compress +#endif + + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left + S->buflen -= BLAKE2S_BLOCKBYTES; + in += fill; + inlen -= fill; + } + else // inlen <= fill + { + memcpy( S->buf + left, in, (size_t)inlen ); + S->buflen += (size_t)inlen; // Be lazy, do not compress + in += inlen; + inlen = 0; + } + } +} + + +void blake2s_final( blake2s_state *S, byte *digest ) +{ + if( S->buflen > BLAKE2S_BLOCKBYTES ) + { + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); + S->buflen -= BLAKE2S_BLOCKBYTES; + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); + } + + blake2s_increment_counter( S, ( uint32 )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( int i = 0; i < 8; ++i ) /* Output full hash */ + RawPut4( S->h[i], digest + 4 * i ); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.hpp new file mode 100644 index 00000000..f88ef378 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s.hpp @@ -0,0 +1,102 @@ +// Based on public domain code written in 2012 by Samuel Neves +#ifndef _RAR_BLAKE2_ +#define _RAR_BLAKE2_ + +#define BLAKE2_DIGEST_SIZE 32 +#define BLAKE2_THREADS_NUMBER 8 + +enum blake2s_constant +{ + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32 +}; + + +// Alignment to 64 improves performance of both SSE and non-SSE versions. +// Alignment to n*16 is required for SSE version, so we selected 64. +// We use the custom alignment scheme instead of __declspec(align(x)), +// because it is less compiler dependent. Also the compiler directive +// does not help if structure is a member of class allocated through +// 'new' operator. +struct blake2s_state +{ + enum { BLAKE_ALIGNMENT = 64 }; + + // buffer and uint32 h[8], t[2], f[2]; + enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; + + byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; + + byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES]. + uint32 *h, *t, *f; // uint32 h[8], t[2], f[2]. + + size_t buflen; + byte last_node; + + blake2s_state() + { + set_pointers(); + } + + // Required when we declare and assign in the same command. + blake2s_state(blake2s_state &st) + { + set_pointers(); + *this=st; + } + + void set_pointers() + { + // Set aligned pointers. Must be done in constructor, not in Init(), + // so assignments like 'blake2sp_state res=blake2ctx' work correctly + // even if blake2sp_init is not called for 'res'. + buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT); + h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES); + t = h + 8; + f = t + 2; + } + + void init() + { + memset( ubuf, 0, sizeof( ubuf ) ); + buflen = 0; + last_node = 0; + } + + // Since we use pointers, the default = would work incorrectly. + blake2s_state& operator = (blake2s_state &st) + { + if (this != &st) + { + memcpy(buf, st.buf, BLAKE_DATA_SIZE); + buflen = st.buflen; + last_node = st.last_node; + } + return *this; + } +}; + + +#ifdef RAR_SMP +class ThreadPool; +#endif + +struct blake2sp_state +{ + blake2s_state S[8]; + blake2s_state R; + byte buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + +#ifdef RAR_SMP + ThreadPool *ThPool; + uint MaxThreads; +#endif +}; + +void blake2sp_init( blake2sp_state *S ); +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ); +void blake2sp_final( blake2sp_state *S, byte *digest ); + +#endif + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s_sse.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s_sse.cpp new file mode 100644 index 00000000..1a02f210 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2s_sse.cpp @@ -0,0 +1,129 @@ +// Based on public domain code written in 2012 by Samuel Neves + +extern const byte blake2s_sigma[10][16]; + +// Initialization vector. +static __m128i blake2s_IV_0_3, blake2s_IV_4_7; + +#ifdef _WIN_64 +// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. +static __m128i crotr8, crotr16; +#endif + +static void blake2s_init_sse() +{ + // We cannot initialize these 128 bit variables in place when declaring + // them globally, because global scope initialization is performed before + // our SSE check and it would make code incompatible with older non-SSE2 + // CPUs. Also we cannot initialize them as static inside of function + // using these variables, because SSE static initialization is not thread + // safe: first thread starts initialization and sets "init done" flag even + // if it is not done yet, second thread can attempt to access half-init + // SSE data. So we moved init code here. + + blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); + blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); + +#ifdef _WIN_64 + crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); + crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); +#endif +} + + +#define LOAD(p) _mm_load_si128( (__m128i *)(p) ) +#define STORE(p,r) _mm_store_si128((__m128i *)(p), r) + +#ifdef _WIN_32 +// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient +// to not use _mm_shuffle_epi8 here. +#define mm_rotr_epi32(r, c) ( \ + _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#else +#define mm_rotr_epi32(r, c) ( \ + c==8 ? _mm_shuffle_epi8(r,crotr8) \ + : c==16 ? _mm_shuffle_epi8(r,crotr16) \ + : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#endif + + +#define G1(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 16); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 12); + +#define G2(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 8); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 7); + +#define DIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) ); + +#define UNDIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); + +#ifdef _WIN_64 + // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load + // from stack operations, which are slower than this code. + #define _mm_set_epi32(i3,i2,i1,i0) \ + _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ + _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) +#endif + +// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode +// and about the same in x64 mode in our test. Perhaps depends on compiler. +// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather +// instructions here, but they did not show any speed gain on i7-6700K. +#define SSE_ROUND(m,row,r) \ +{ \ + __m128i buf; \ + buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + DIAGONALIZE(row[0],row[1],row[2],row[3]); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \ +} + + +static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + __m128i row[4]; + __m128i ff0, ff1; + + const uint32 *m = ( uint32 * )block; + + row[0] = ff0 = LOAD( &S->h[0] ); + row[1] = ff1 = LOAD( &S->h[4] ); + + row[2] = blake2s_IV_0_3; + row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) ); + SSE_ROUND( m, row, 0 ); + SSE_ROUND( m, row, 1 ); + SSE_ROUND( m, row, 2 ); + SSE_ROUND( m, row, 3 ); + SSE_ROUND( m, row, 4 ); + SSE_ROUND( m, row, 5 ); + SSE_ROUND( m, row, 6 ); + SSE_ROUND( m, row, 7 ); + SSE_ROUND( m, row, 8 ); + SSE_ROUND( m, row, 9 ); + STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) ); + STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) ); + return 0; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/blake2sp.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2sp.cpp new file mode 100644 index 00000000..da645883 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/blake2sp.cpp @@ -0,0 +1,153 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Written in 2012 by Samuel Neves <sneves@dei.uc.pt> + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. +*/ + +#define PARALLELISM_DEGREE 8 + +void blake2sp_init( blake2sp_state *S ) +{ + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + + blake2s_init_param( &S->R, 0, 1 ); // Init root. + + for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. + + S->R.last_node = 1; + S->S[PARALLELISM_DEGREE - 1].last_node = 1; +} + + +struct Blake2ThreadData +{ + void Update(); + blake2s_state *S; + const byte *in; + size_t inlen; +}; + + +void Blake2ThreadData::Update() +{ + size_t inlen__ = inlen; + const byte *in__ = ( const byte * )in; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { +#ifdef USE_SSE + // We gain 5% in i7 SSE mode by prefetching next data block. + if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) + _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); +#endif + blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } +} + +#ifdef RAR_SMP +THREAD_PROC(Blake2Thread) +{ + Blake2ThreadData *td=(Blake2ThreadData *)Data; + td->Update(); +} +#endif + + +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ) +{ + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + + Blake2ThreadData btd_array[PARALLELISM_DEGREE]; + +#ifdef RAR_SMP + uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads; + + if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here. + ThreadNumber=4; +#else + uint ThreadNumber=1; +#endif + + for (size_t id__=0;id__<PARALLELISM_DEGREE;) + { + for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++) + { + Blake2ThreadData *btd=btd_array+Thread; + + btd->inlen = inlen; + btd->in = in + id__ * BLAKE2S_BLOCKBYTES; + btd->S = &S->S[id__]; + +#ifdef RAR_SMP + if (ThreadNumber>1) + S->ThPool->AddTask(Blake2Thread,(void*)btd); + else + btd->Update(); +#else + btd->Update(); +#endif + id__++; + } +#ifdef RAR_SMP + if (S->ThPool!=NULL) // Can be NULL in -mt1 mode. + S->ThPool->WaitDone(); +#endif // RAR_SMP + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, (size_t)inlen ); + + S->buflen = left + (size_t)inlen; +} + + +void blake2sp_final( blake2sp_state *S, byte *digest ) +{ + byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( &S->S[i], hash[i] ); + } + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES ); + + blake2s_final( &S->R, digest ); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.cpp new file mode 100644 index 00000000..82d24f44 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.cpp @@ -0,0 +1,1113 @@ +#include "rar.hpp" + +#include "cmdfilter.cpp" +#include "cmdmix.cpp" + +CommandData::CommandData() +{ + Init(); +} + + +void CommandData::Init() +{ + RAROptions::Init(); + + *Command=0; + *ArcName=0; + FileLists=false; + NoMoreSwitches=false; + + ListMode=RCLM_AUTO; + + BareOutput=false; + + + FileArgs.Reset(); + ExclArgs.Reset(); + InclArgs.Reset(); + StoreArgs.Reset(); + ArcNames.Reset(); + NextVolSizes.Reset(); +} + + +// Return the pointer to next position in the string and store dynamically +// allocated command line parameter in Par. +static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) +{ + const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); + if (NextCmd==NULL) + return NULL; + size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. + *Par=(wchar *)malloc(ParSize*sizeof(wchar)); + if (*Par==NULL) + return NULL; + return GetCmdParam(CmdLine,*Par,ParSize); +} + + +#if !defined(SFX_MODULE) +void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) +{ + *Command=0; + NoMoreSwitches=false; +#ifdef CUSTOM_CMDLINE_PARSER + // In Windows we may prefer to implement our own command line parser + // to avoid replacing \" by " in standard parser. Such replacing corrupts + // destination paths like "dest path\" in extraction commands. + const wchar *CmdLine=GetCommandLine(); + + wchar *Par; + for (bool FirstParam=true;;FirstParam=false) + { + if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) + break; + if (!FirstParam) // First parameter is the executable name. + if (Preprocess) + PreprocessArg(Par); + else + ParseArg(Par); + free(Par); + } +#else + Array<wchar> Arg; + for (int I=1;I<argc;I++) + { + Arg.Alloc(strlen(argv[I])+1); + CharToWide(argv[I],&Arg[0],Arg.Size()); + if (Preprocess) + PreprocessArg(&Arg[0]); + else + ParseArg(&Arg[0]); + } +#endif + if (!Preprocess) + ParseDone(); +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ParseArg(wchar *Arg) +{ + if (IsSwitch(*Arg) && !NoMoreSwitches) + if (Arg[1]=='-' && Arg[2]==0) + NoMoreSwitches=true; + else + ProcessSwitch(Arg+1); + else + if (*Command==0) + { + wcsncpyz(Command,Arg,ASIZE(Command)); + + + *Command=toupperw(*Command); + // 'I' and 'S' commands can contain case sensitive strings after + // the first character, so we must not modify their case. + // 'S' can contain SFX name, which case is important in Unix. + if (*Command!='I' && *Command!='S') + wcsupper(Command); + if (*Command=='P') // Enforce -idq for print command. + { + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); + } + } + else + if (*ArcName==0) + wcsncpyz(ArcName,Arg,ASIZE(ArcName)); + else + { + // Check if last character is the path separator. + size_t Length=wcslen(Arg); + wchar EndChar=Length==0 ? 0:Arg[Length-1]; + bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar); + + wchar CmdChar=toupperw(*Command); + bool Add=wcschr(L"AFUM",CmdChar)!=NULL; + bool Extract=CmdChar=='X' || CmdChar=='E'; + bool Repair=CmdChar=='R' && Command[1]==0; + if (EndSeparator && !Add) + wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); + else + if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS)) + FileArgs.AddString(Arg); + else + { + FindData FileData; + bool Found=FindFile::FastFind(Arg,&FileData); + if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && + ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1)) + { + FileLists=true; + + ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true); + + } + else // We use 'destpath\' when extracting and reparing. + if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0) + { + wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); + } + else + FileArgs.AddString(Arg); + } + } +} +#endif + + +void CommandData::ParseDone() +{ + if (FileArgs.ItemsCount()==0 && !FileLists) + FileArgs.AddString(MASKALL); + wchar CmdChar=toupperw(Command[0]); + bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P'; + if (Test && Extract) + Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands. + + // Suppress the copyright message and final end of line for 'lb' and 'vb'. + if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B') + BareOutput=true; +} + + +#if !defined(SFX_MODULE) +void CommandData::ParseEnvVar() +{ + char *EnvStr=getenv("RAR"); + if (EnvStr!=NULL) + { + Array<wchar> EnvStrW(strlen(EnvStr)+1); + CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size()); + ProcessSwitchesString(&EnvStrW[0]); + } +} +#endif + + + +#if !defined(SFX_MODULE) +// Preprocess those parameters, which must be processed before the rest of +// command line. Return 'false' to stop further processing. +void CommandData::PreprocessArg(const wchar *Arg) +{ + if (IsSwitch(Arg[0]) && !NoMoreSwitches) + { + Arg++; + if (Arg[0]=='-' && Arg[1]==0) // Switch "--". + NoMoreSwitches=true; + if (wcsicomp(Arg,L"cfg-")==0) + ConfigDisabled=true; + if (wcsnicomp(Arg,L"ilog",4)==0) + { + // Ensure that correct log file name is already set + // if we need to report an error when processing the command line. + ProcessSwitch(Arg); + InitLogOptions(LogName,ErrlogCharset); + } + if (wcsnicomp(Arg,L"sc",2)==0) + { + // Process -sc before reading any file lists. + ProcessSwitch(Arg); + if (*LogName!=0) + InitLogOptions(LogName,ErrlogCharset); + } + } + else + if (*Command==0) + wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini. +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ReadConfig() +{ + StringList List; + if (ReadTextFile(DefConfigName,&List,true)) + { + wchar *Str; + while ((Str=List.GetString())!=NULL) + { + while (IsSpace(*Str)) + Str++; + if (wcsnicomp(Str,L"switches=",9)==0) + ProcessSwitchesString(Str+9); + if (*Command!=0) + { + wchar Cmd[16]; + wcsncpyz(Cmd,Command,ASIZE(Cmd)); + wchar C0=toupperw(Cmd[0]); + wchar C1=toupperw(Cmd[1]); + if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V') + Cmd[1]=0; + if (C0=='R' && (C1=='R' || C1=='V')) + Cmd[2]=0; + wchar SwName[16+ASIZE(Cmd)]; + swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd); + size_t Length=wcslen(SwName); + if (wcsnicomp(Str,SwName,Length)==0) + ProcessSwitchesString(Str+Length); + } + } + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitchesString(const wchar *Str) +{ + wchar *Par; + while ((Str=AllocCmdParam(Str,&Par))!=NULL) + { + if (IsSwitch(*Par)) + ProcessSwitch(Par+1); + free(Par); + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitch(const wchar *Switch) +{ + + switch(toupperw(Switch[0])) + { + case '@': + ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; + break; + case 'A': + switch(toupperw(Switch[1])) + { + case 'C': + ClearArc=true; + break; + case 'D': + if (Switch[2]==0) + AppendArcNameToPath=APPENDARCNAME_DESTPATH; + else + if (Switch[2]=='1') + AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR; + else + if (Switch[2]=='2') + AppendArcNameToPath=APPENDARCNAME_OWNDIR; + break; +#ifndef SFX_MODULE + case 'G': + if (Switch[2]=='-' && Switch[3]==0) + GenerateArcName=0; + else + if (toupperw(Switch[2])=='F') + wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask)); + else + { + GenerateArcName=true; + wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } + break; +#endif + case 'I': + IgnoreGeneralAttr=true; + break; + case 'N': // Reserved for archive name. + break; + case 'O': + AddArcOnly=true; + break; + case 'P': + wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath)); + break; + case 'S': + SyncFiles=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'C': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case '-': + DisableComment=true; + break; + case 'U': + ConvertNames=NAMES_UPPERCASE; + break; + case 'L': + ConvertNames=NAMES_LOWERCASE; + break; + } + break; + case 'D': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case 'S': + DisableSortSolid=true; + break; + case 'H': + OpenShared=true; + break; + case 'F': + DeleteFiles=true; + break; + } + break; + case 'E': + switch(toupperw(Switch[1])) + { + case 'P': + switch(Switch[2]) + { + case 0: + ExclPath=EXCL_SKIPWHOLEPATH; + break; + case '1': + ExclPath=EXCL_BASEPATH; + break; + case '2': + ExclPath=EXCL_SAVEFULLPATH; + break; + case '3': + ExclPath=EXCL_ABSPATH; + break; + case '4': + wcsncpyz(ExclArcPath,Switch+3,ASIZE(ExclArcPath)); + break; + } + break; + default: + if (Switch[1]=='+') + { + InclFileAttr|=GetExclAttr(Switch+2,InclDir); + InclAttrSet=true; + } + else + ExclFileAttr|=GetExclAttr(Switch+1,ExclDir); + break; + } + break; + case 'F': + if (Switch[1]==0) + FreshFiles=true; + else + BadSwitch(Switch); + break; + case 'H': + switch (toupperw(Switch[1])) + { + case 'P': + EncryptHeaders=true; + if (Switch[2]!=0) + { + if (wcslen(Switch+2)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + Password.Set(Switch+2); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } + else + if (!Password.IsSet()) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + break; + default : + BadSwitch(Switch); + break; + } + break; + case 'I': + if (wcsnicomp(Switch+1,L"LOG",3)==0) + { + wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); + break; + } + if (wcsnicomp(Switch+1,L"SND",3)==0) + { + Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON; + break; + } + if (wcsicomp(Switch+1,L"ERR")==0) + { + MsgStream=MSG_STDERR; + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleMsgStream(MSG_STDERR); + break; + } + if (wcsnicomp(Switch+1,L"EML",3)==0) + { + wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); + break; + } + if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv. + { + VerboseOutput=true; + break; + } + if (wcsicomp(Switch+1,L"NUL")==0) + { + MsgStream=MSG_NULL; + SetConsoleMsgStream(MSG_NULL); + break; + } + if (toupperw(Switch[1])=='D') + { + for (uint I=2;Switch[I]!=0;I++) + switch(toupperw(Switch[I])) + { + case 'Q': + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); + break; + case 'C': + DisableCopyright=true; + break; + case 'D': + DisableDone=true; + break; + case 'P': + DisablePercentage=true; + break; + case 'N': + DisableNames=true; + break; + case 'V': + VerboseOutput=true; + break; + } + break; + } + if (wcsnicomp(Switch+1,L"OFF",3)==0) + { + switch(Switch[4]) + { + case 0: + case '1': + Shutdown=POWERMODE_OFF; + break; + case '2': + Shutdown=POWERMODE_HIBERNATE; + break; + case '3': + Shutdown=POWERMODE_SLEEP; + break; + case '4': + Shutdown=POWERMODE_RESTART; + break; + } + break; + } + if (wcsicomp(Switch+1,L"VER")==0) + { + PrintVersion=true; + break; + } + break; + case 'K': + switch(toupperw(Switch[1])) + { + case 'B': + KeepBroken=true; + break; + case 0: + Lock=true; + break; + } + break; + case 'M': + switch(toupperw(Switch[1])) + { + case 'C': + { + const wchar *Str=Switch+2; + if (*Str=='-') + for (uint I=0;I<ASIZE(FilterModes);I++) + FilterModes[I].State=FILTER_DISABLE; + else + while (*Str!=0) + { + int Param1=0,Param2=0; + FilterState State=FILTER_AUTO; + FilterType Type=FILTER_NONE; + if (IsDigit(*Str)) + { + Param1=atoiw(Str); + while (IsDigit(*Str)) + Str++; + } + if (*Str==':' && IsDigit(Str[1])) + { + Param2=atoiw(++Str); + while (IsDigit(*Str)) + Str++; + } + switch(toupperw(*(Str++))) + { + case 'T': Type=FILTER_PPM; break; + case 'E': Type=FILTER_E8; break; + case 'D': Type=FILTER_DELTA; break; + case 'A': Type=FILTER_AUDIO; break; + case 'C': Type=FILTER_RGB; break; + case 'R': Type=FILTER_ARM; break; + } + if (*Str=='+' || *Str=='-') + State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE; + FilterModes[Type].State=State; + FilterModes[Type].Param1=Param1; + FilterModes[Type].Param2=Param2; + } + } + break; + case 'M': + break; + case 'D': + break; + case 'E': + if (toupperw(Switch[2])=='S' && Switch[3]==0) + SkipEncrypted=true; + break; + case 'S': + { + wchar StoreNames[1024]; + wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames)); + wchar *Names=StoreNames; + while (*Names!=0) + { + wchar *End=wcschr(Names,';'); + if (End!=NULL) + *End=0; + if (*Names=='.') + Names++; + wchar Mask[NM]; + if (wcspbrk(Names,L"*?.")==NULL) + swprintf(Mask,ASIZE(Mask),L"*.%ls",Names); + else + wcsncpyz(Mask,Names,ASIZE(Mask)); + StoreArgs.AddString(Mask); + if (End==NULL) + break; + Names=End+1; + } + } + break; +#ifdef RAR_SMP + case 'T': + Threads=atoiw(Switch+2); + if (Threads>MaxPoolThreads || Threads<1) + BadSwitch(Switch); + else + { + } + break; +#endif + default: + Method=Switch[1]-'0'; + if (Method>5 || Method<0) + BadSwitch(Switch); + break; + } + break; + case 'N': + case 'X': + if (Switch[1]!=0) + { + StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; + if (Switch[1]=='@' && !IsWildcard(Switch)) + ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true); + else + Args->AddString(Switch+1); + } + break; + case 'O': + switch(toupperw(Switch[1])) + { + case '+': + Overwrite=OVERWRITE_ALL; + break; + case '-': + Overwrite=OVERWRITE_NONE; + break; + case 0: + Overwrite=OVERWRITE_FORCE_ASK; + break; +#ifdef _WIN_ALL + case 'C': + SetCompressedAttr=true; + break; +#endif + case 'H': + SaveHardLinks=true; + break; + + +#ifdef SAVE_LINKS + case 'L': + SaveSymLinks=true; + if (toupperw(Switch[2])=='A') + AbsoluteLinks=true; + break; +#endif +#ifdef _WIN_ALL + case 'N': + if (toupperw(Switch[2])=='I') + AllowIncompatNames=true; + break; +#endif + case 'P': + wcsncpyz(ExtrPath,Switch+2,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); + break; + case 'R': + Overwrite=OVERWRITE_AUTORENAME; + break; +#ifdef _WIN_ALL + case 'S': + SaveStreams=true; + break; +#endif + case 'W': + ProcessOwners=true; + break; + default : + BadSwitch(Switch); + break; + } + break; + case 'P': + if (Switch[1]==0) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + else + { + if (wcslen(Switch+1)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + Password.Set(Switch+1); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } + break; +#ifndef SFX_MODULE + case 'Q': + if (toupperw(Switch[1])=='O') + switch(toupperw(Switch[2])) + { + case 0: + QOpenMode=QOPEN_AUTO; + break; + case '-': + QOpenMode=QOPEN_NONE; + break; + case '+': + QOpenMode=QOPEN_ALWAYS; + break; + default: + BadSwitch(Switch); + break; + } + else + BadSwitch(Switch); + break; +#endif + case 'R': + switch(toupperw(Switch[1])) + { + case 0: + Recurse=RECURSE_ALWAYS; + break; + case '-': + Recurse=RECURSE_DISABLE; + break; + case '0': + Recurse=RECURSE_WILDCARDS; + break; + case 'I': + { + Priority=atoiw(Switch+2); + if (Priority<0 || Priority>15) + BadSwitch(Switch); + const wchar *ChPtr=wcschr(Switch+2,':'); + if (ChPtr!=NULL) + { + SleepTime=atoiw(ChPtr+1); + if (SleepTime>1000) + BadSwitch(Switch); + InitSystemOptions(SleepTime); + } + SetPriority(Priority); + } + break; + } + break; + case 'S': + if (IsDigit(Switch[1])) + { + Solid|=SOLID_COUNT; + SolidCount=atoiw(&Switch[1]); + } + else + switch(toupperw(Switch[1])) + { + case 0: + Solid|=SOLID_NORMAL; + break; + case '-': + Solid=SOLID_NONE; + break; + case 'E': + Solid|=SOLID_FILEEXT; + break; + case 'V': + Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT; + break; + case 'D': + Solid|=SOLID_VOLUME_DEPENDENT; + break; + case 'I': + ProhibitConsoleInput(); + wcsncpyz(UseStdin,Switch[2] ? Switch+2:L"stdin",ASIZE(UseStdin)); + break; + case 'L': + if (IsDigit(Switch[2])) + FileSizeLess=atoilw(Switch+2); + break; + case 'M': + if (IsDigit(Switch[2])) + FileSizeMore=atoilw(Switch+2); + break; + case 'C': + { + bool AlreadyBad=false; // Avoid reporting "bad switch" several times. + + RAR_CHARSET rch=RCH_DEFAULT; + switch(toupperw(Switch[2])) + { + case 'A': + rch=RCH_ANSI; + break; + case 'O': + rch=RCH_OEM; + break; + case 'U': + rch=RCH_UNICODE; + break; + case 'F': + rch=RCH_UTF8; + break; + default : + BadSwitch(Switch); + AlreadyBad=true; + break; + }; + if (!AlreadyBad) + if (Switch[3]==0) + CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; + else + for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) + switch(toupperw(Switch[I])) + { + case 'C': + CommentCharset=rch; + break; + case 'L': + FilelistCharset=rch; + break; + case 'R': + RedirectCharset=rch; + break; + default: + BadSwitch(Switch); + AlreadyBad=true; + break; + } + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleRedirectCharset(RedirectCharset); + } + break; + + } + break; + case 'T': + switch(toupperw(Switch[1])) + { + case 'K': + ArcTime=ARCTIME_KEEP; + break; + case 'L': + ArcTime=ARCTIME_LATEST; + break; + case 'O': + SetTimeFilters(Switch+2,true,true); + break; + case 'N': + SetTimeFilters(Switch+2,false,true); + break; + case 'B': + SetTimeFilters(Switch+2,true,false); + break; + case 'A': + SetTimeFilters(Switch+2,false,false); + break; + case 'S': + SetStoreTimeMode(Switch+2); + break; + case '-': + Test=false; + break; + case 0: + Test=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'U': + if (Switch[1]==0) + UpdateFiles=true; + else + BadSwitch(Switch); + break; + case 'V': + switch(toupperw(Switch[1])) + { + case 'P': + VolumePause=true; + break; + case 'E': + if (toupperw(Switch[2])=='R') + VersionControl=atoiw(Switch+3)+1; + break; + case '-': + VolSize=0; + break; + default: + VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. + break; + } + break; + case 'W': + wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); + AddEndSlash(TempPath,ASIZE(TempPath)); + break; + case 'Y': + AllYes=true; + break; + case 'Z': + if (Switch[1]==0) + { + // If comment file is not specified, we read data from stdin. + wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile)); + } + else + wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); + break; + case '?' : + OutHelp(RARX_SUCCESS); + break; + default : + BadSwitch(Switch); + break; + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::BadSwitch(const wchar *Switch) +{ + mprintf(St(MUnknownOption),Switch); + ErrHandler.Exit(RARX_USERERROR); +} +#endif + + +void CommandData::ProcessCommand() +{ +#ifndef SFX_MODULE + + const wchar *SingleCharCommands=L"FUADPXETK"; + if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) + OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. + + const wchar *ArcExt=GetExt(ArcName); +#ifdef _UNIX + if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); +#else + if (ArcExt==NULL) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); +#endif + // Treat arcname.part1 as arcname.part1.rar. + if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) && + !FileExist(ArcName)) + { + wchar Name[NM]; + wcsncpyz(Name,ArcName,ASIZE(Name)); + wcsncatz(Name,L".rar",ASIZE(Name)); + if (FileExist(Name)) + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + } + + if (wcschr(L"AFUMD",*Command)==NULL && *UseStdin==0) + { + if (GenerateArcName) + { + const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask; + GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false); + } + + StringList ArcMasks; + ArcMasks.AddString(ArcName); + ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); + FindData FindData; + while (Scan.GetNext(&FindData)==SCAN_SUCCESS) + AddArcName(FindData.Name); + } + else + AddArcName(ArcName); +#endif + + switch(Command[0]) + { + case 'P': + case 'X': + case 'E': + case 'T': + { + CmdExtract Extract(this); + Extract.DoExtract(); + } + break; +#ifndef SILENT + case 'V': + case 'L': + ListArchive(this); + break; + default: + OutHelp(RARX_USERERROR); +#endif + } + if (!BareOutput) + mprintf(L"\n"); +} + + +void CommandData::AddArcName(const wchar *Name) +{ + ArcNames.AddString(Name); +} + + +bool CommandData::GetArcName(wchar *Name,int MaxSize) +{ + return ArcNames.GetString(Name,MaxSize); +} + + +bool CommandData::IsSwitch(int Ch) +{ +#if defined(_WIN_ALL) || defined(_EMX) + return Ch=='-' || Ch=='/'; +#else + return Ch=='-'; +#endif +} + + +#ifndef SFX_MODULE +uint CommandData::GetExclAttr(const wchar *Str,bool &Dir) +{ + if (IsDigit(*Str)) + return wcstol(Str,NULL,0); + + uint Attr=0; + while (*Str!=0) + { + switch(toupperw(*Str)) + { + case 'D': + Dir=true; + break; +#ifdef _UNIX + case 'V': + Attr|=S_IFCHR; + break; +#elif defined(_WIN_ALL) || defined(_EMX) + case 'R': + Attr|=0x1; + break; + case 'H': + Attr|=0x2; + break; + case 'S': + Attr|=0x4; + break; + case 'A': + Attr|=0x20; + break; +#endif + } + Str++; + } + return Attr; +} +#endif + + + + +#ifndef SFX_MODULE +bool CommandData::CheckWinSize() +{ + // Define 0x100000000 as macro to avoid troubles with older compilers. + const uint64 MaxDictSize=INT32TO64(1,0); + // Limit the dictionary size to 4 GB. + for (uint64 I=0x10000;I<=MaxDictSize;I*=2) + if (WinSize==I) + return true; + WinSize=0x400000; + return false; +} +#endif + + +#ifndef SFX_MODULE +void CommandData::ReportWrongSwitches(RARFORMAT Format) +{ + if (Format==RARFMT15) + { + if (HashType!=HASH_CRC32) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4); +#ifdef _WIN_ALL + if (SaveSymLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4); +#endif + if (SaveHardLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4); + +#ifdef _WIN_ALL + // Do not report a wrong dictionary size here, because we are not sure + // yet about archive format. We can switch to RAR5 mode later + // if we update RAR5 archive. + + +#endif + if (QOpenMode!=QOPEN_AUTO) + uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4); + } + if (Format==RARFMT50) + { + } +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.hpp new file mode 100644 index 00000000..2a219220 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/cmddata.hpp @@ -0,0 +1,70 @@ +#ifndef _RAR_CMDDATA_ +#define _RAR_CMDDATA_ + + +#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" + +enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; + +enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1}; + +class CommandData:public RAROptions +{ + private: + void ProcessSwitch(const wchar *Switch); + void BadSwitch(const wchar *Switch); + uint GetExclAttr(const wchar *Str,bool &Dir); +#if !defined(SFX_MODULE) + void SetTimeFilters(const wchar *Mod,bool Before,bool Age); + void SetStoreTimeMode(const wchar *S); +#endif + + bool FileLists; + bool NoMoreSwitches; + RAR_CMD_LIST_MODE ListMode; + bool BareOutput; + public: + CommandData(); + void Init(); + + void ParseCommandLine(bool Preprocess,int argc, char *argv[]); + void ParseArg(wchar *ArgW); + void ParseDone(); + void ParseEnvVar(); + void ReadConfig(); + void PreprocessArg(const wchar *Arg); + void ProcessSwitchesString(const wchar *Str); + void OutTitle(); + void OutHelp(RAR_EXIT ExitCode); + bool IsSwitch(int Ch); + bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList); + static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode); + bool ExclDirByAttr(uint FileAttr); + bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta); + bool SizeCheck(int64 Size); + bool AnyFiltersActive(); + int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + bool Flags,wchar *MatchedArg,uint MatchedArgSize); + void ProcessCommand(); + void AddArcName(const wchar *Name); + bool GetArcName(wchar *Name,int MaxSize); + bool CheckWinSize(); + + int GetRecoverySize(const wchar *CmdStr,const wchar *Value,int DefSize); + +#ifndef SFX_MODULE + void ReportWrongSwitches(RARFORMAT Format); +#endif + + wchar Command[NM+16]; + + wchar ArcName[NM]; + + StringList FileArgs; + StringList ExclArgs; + StringList InclArgs; + StringList ArcNames; + StringList StoreArgs; +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/cmdfilter.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/cmdfilter.cpp new file mode 100644 index 00000000..d098d108 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/cmdfilter.cpp @@ -0,0 +1,354 @@ +// Return 'true' if we need to exclude the file from processing as result +// of -x switch. If CheckInclList is true, we also check the file against +// the include list created with -n switch. +bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList) +{ + if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) + return true; + if (!CheckInclList || InclArgs.ItemsCount()==0) + return false; + if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) + return false; + return true; +} + + +bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode) +{ + wchar *Name=ConvertPath(CheckName,NULL,0); + wchar FullName[NM]; + wchar CurMask[NM]; + *FullName=0; + Args->Rewind(); + while (Args->GetString(CurMask,ASIZE(CurMask))) + { + wchar *LastMaskChar=PointToLastChar(CurMask); + bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + + if (Dir) + { + // CheckName is a directory. + if (DirMask) + { + // We process the directory and have the directory exclusion mask. + // So let's convert "mask\" to "mask" and process it normally. + + *LastMaskChar=0; + } + else + { + // REMOVED, we want -npath\* to match empty folders too. + // If mask has wildcards in name part and does not have the trailing + // '\' character, we cannot use it for directories. + + // if (IsWildcard(PointToName(CurMask))) + // continue; + } + } + else + { + // If we process a file inside of directory excluded by "dirmask\". + // we want to exclude such file too. So we convert "dirmask\" to + // "dirmask\*". It is important for operations other than archiving + // with -x. When archiving with -x, directory matched by "dirmask\" + // is excluded from further scanning. + + if (DirMask) + wcsncatz(CurMask,L"*",ASIZE(CurMask)); + } + +#ifndef SFX_MODULE + if (CheckFullPath && IsFullPath(CurMask)) + { + // We do not need to do the special "*\" processing here, because + // unlike the "else" part of this "if", now we convert names to full + // format, so they all include the path, which is matched by "*\" + // correctly. Moreover, removing "*\" from mask would break + // the comparison, because now all names have the path. + + if (*FullName==0) + ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); + if (CmpName(CurMask,FullName,MatchMode)) + return true; + } + else +#endif + { + wchar NewName[NM+2],*CurName=Name; + + // Important to convert before "*\" check below, so masks like + // d:*\something are processed properly. + wchar *CmpMask=ConvertPath(CurMask,NULL,0); + + if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) + { + // We want "*\name" to match 'name' not only in subdirectories, + // but also in the current directory. We convert the name + // from 'name' to '.\name' to be matched by "*\" part even if it is + // in current directory. + NewName[0]='.'; + NewName[1]=CPATHDIVIDER; + wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); + CurName=NewName; + } + + if (CmpName(CmpMask,CurName,MatchMode)) + return true; + } + } + return false; +} + + + + +#ifndef SFX_MODULE +// Now this function performs only one task and only in Windows version: +// it skips symlinks to directories if -e1024 switch is specified. +// Symlinks are skipped in ScanTree class, so their entire contents +// is skipped too. Without this function we would check the attribute +// only directly before archiving, so we would skip the symlink record, +// but not the contents of symlinked directory. +bool CommandData::ExclDirByAttr(uint FileAttr) +{ +#ifdef _WIN_ALL + if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && + (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + return true; +#endif + return false; +} +#endif + + + + +#if !defined(SFX_MODULE) +void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age) +{ + bool ModeOR=false,TimeMods=false; + const wchar *S=Mod; + // Check if any 'mca' modifiers are present, set OR mode if 'o' is present, + // skip modifiers and set S to beginning of time string. Be sure to check + // *S!=0, because termination 0 is a part of string for wcschr. + for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++) + if (*S=='o' || *S=='O') + ModeOR=true; + else + TimeMods=true; + + if (!TimeMods) // Assume 'm' if no modifiers are specified. + Mod=L"m"; + + // Set the specified time for every modifier. Be sure to check *Mod!=0, + // because termination 0 is a part of string for wcschr. This check is + // important when we set Mod to "m" above. + for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++) + switch(toupperw(*Mod)) + { + case 'M': + if (Before) + { + Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S); + FileMtimeBeforeOR=ModeOR; + } + else + { + Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S); + FileMtimeAfterOR=ModeOR; + } + break; + case 'C': + if (Before) + { + Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S); + FileCtimeBeforeOR=ModeOR; + } + else + { + Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S); + FileCtimeAfterOR=ModeOR; + } + break; + case 'A': + if (Before) + { + Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S); + FileAtimeBeforeOR=ModeOR; + } + else + { + Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S); + FileAtimeAfterOR=ModeOR; + } + break; + } +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta) +{ + bool FilterOR=false; + + if (FileMtimeBefore.IsSet()) // Filter present. + if (ftm>=FileMtimeBefore) // Condition not matched. + if (FileMtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileMtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileMtimeAfter.IsSet()) // Filter present. + if (ftm<FileMtimeAfter) // Condition not matched. + if (FileMtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileMtimeAfterOR) + return false; // Include file in OR mode. + + if (FileCtimeBefore.IsSet()) // Filter present. + if (ftc>=FileCtimeBefore) // Condition not matched. + if (FileCtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileCtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileCtimeAfter.IsSet()) // Filter present. + if (ftc<FileCtimeAfter) // Condition not matched. + if (FileCtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileCtimeAfterOR) + return false; // Include file in OR mode. + + if (FileAtimeBefore.IsSet()) // Filter present. + if (fta>=FileAtimeBefore) // Condition not matched. + if (FileAtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileAtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileAtimeAfter.IsSet()) // Filter present. + if (fta<FileAtimeAfter) // Condition not matched. + if (FileAtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileAtimeAfterOR) + return false; // Include file in OR mode. + + return FilterOR; // Exclude if all OR filters are not matched. +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::SizeCheck(int64 Size) +{ + if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size. + return false; + if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) + return true; + if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) + return true; + return false; +} +#endif + + + + +// Return 0 if file must not be processed or a number of matched parameter otherwise. +int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + bool Flags,wchar *MatchedArg,uint MatchedArgSize) +{ + if (MatchedArg!=NULL && MatchedArgSize>0) + *MatchedArg=0; + bool Dir=FileHead.Dir; + if (ExclCheck(FileHead.FileName,Dir,false,true)) + return 0; +#ifndef SFX_MODULE + if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime)) + return 0; + if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) + return 0; + if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 || + FileHead.Dir && !InclDir)) + return 0; + if (!Dir && SizeCheck(FileHead.UnpSize)) + return 0; +#endif + wchar *ArgName; + FileArgs.Rewind(); + for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) + if (CmpName(ArgName,FileHead.FileName,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; + if (MatchedArg!=NULL) + wcsncpyz(MatchedArg,ArgName,MatchedArgSize); + return StringCount; + } + return 0; +} + + +#if !defined(SFX_MODULE) +void CommandData::SetStoreTimeMode(const wchar *S) +{ + if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+') + { + // Apply -ts, -ts1, -ts-, -ts+ to all 3 times. + // Handle obsolete -ts[2,3,4] as ts+. + EXTTIME_MODE Mode=EXTTIME_MAX; + if (*S=='-') + Mode=EXTTIME_NONE; + if (*S=='1') + Mode=EXTTIME_1S; + xmtime=xctime=xatime=Mode; + S++; + } + + while (*S!=0) + { + EXTTIME_MODE Mode=EXTTIME_MAX; + if (S[1]=='-') + Mode=EXTTIME_NONE; + if (S[1]=='1') + Mode=EXTTIME_1S; + switch(toupperw(*S)) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + case 'P': + PreserveAtime=true; + break; + } + S++; + } +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/cmdmix.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/cmdmix.cpp new file mode 100644 index 00000000..1fc4b8ca --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/cmdmix.cpp @@ -0,0 +1,118 @@ +void CommandData::OutTitle() +{ + if (BareOutput || DisableCopyright) + return; +#if defined(__GNUC__) && defined(SFX_MODULE) + mprintf(St(MCopyrightS)); +#else +#ifndef SILENT + static bool TitleShown=false; + if (TitleShown) + return; + TitleShown=true; + + wchar Version[80]; + if (RARVER_BETA!=0) + swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); + else + swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); +#if defined(_WIN_32) || defined(_WIN_64) + wcsncatz(Version,L" ",ASIZE(Version)); +#endif +#ifdef _WIN_32 + wcsncatz(Version,St(Mx86),ASIZE(Version)); +#endif +#ifdef _WIN_64 + wcsncatz(Version,St(Mx64),ASIZE(Version)); +#endif + if (PrintVersion) + { + mprintf(L"%s",Version); + exit(0); + } + mprintf(St(MUCopyright),Version,RARVER_YEAR); +#endif +#endif +} + + +inline bool CmpMSGID(MSGID i1,MSGID i2) +{ +#ifdef MSGID_INT + return i1==i2; +#else + // If MSGID is const char*, we cannot compare pointers only. + // Pointers to different instances of same string can differ, + // so we need to compare complete strings. + return wcscmp(i1,i2)==0; +#endif +} + +void CommandData::OutHelp(RAR_EXIT ExitCode) +{ +#if !defined(SILENT) + OutTitle(); + static MSGID Help[]={ +#ifdef SFX_MODULE + // Console SFX switches definition. + MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV +#else + // UnRAR switches definition. + MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, + MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, + MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP, + MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwN,MCHelpSwNa, + MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOP,MCHelpSwOR, + MCHelpSwOW,MCHelpSwP,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSI, + MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO, + MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX, + MCHelpSwXa,MCHelpSwXal,MCHelpSwY +#endif + }; + + for (uint I=0;I<ASIZE(Help);I++) + { +#ifndef SFX_MODULE + if (CmpMSGID(Help[I],MCHelpSwV)) + continue; +#ifndef _WIN_ALL + static MSGID Win32Only[]={ + MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF, + MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI + }; + bool Found=false; + for (uint J=0;J<ASIZE(Win32Only);J++) + if (CmpMSGID(Help[I],Win32Only[J])) + { + Found=true; + break; + } + if (Found) + continue; +#endif +#if !defined(_UNIX) && !defined(_WIN_ALL) + if (CmpMSGID(Help[I],MCHelpSwOW)) + continue; +#endif +#if !defined(_WIN_ALL) && !defined(_EMX) + if (CmpMSGID(Help[I],MCHelpSwAC)) + continue; +#endif +#ifndef SAVE_LINKS + if (CmpMSGID(Help[I],MCHelpSwOL)) + continue; +#endif +#ifndef RAR_SMP + if (CmpMSGID(Help[I],MCHelpSwMT)) + continue; +#endif +#endif + mprintf(St(Help[I])); + } + mprintf(L"\n"); + ErrHandler.Exit(ExitCode); +#endif +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/coder.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/coder.cpp new file mode 100644 index 00000000..9d971a8a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/coder.cpp @@ -0,0 +1,48 @@ + + +inline unsigned int RangeCoder::GetChar() +{ + return(UnpackRead->GetChar()); +} + + +void RangeCoder::InitDecoder(Unpack *UnpackRead) +{ + RangeCoder::UnpackRead=UnpackRead; + + low=code=0; + range=uint(-1); + for (int i=0;i < 4;i++) + code=(code << 8) | GetChar(); +} + + +// (int) cast before "low" added only to suppress compiler warnings. +#define ARI_DEC_NORMALIZE(code,low,range,read) \ +{ \ + while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \ + { \ + code=(code << 8) | read->GetChar(); \ + range <<= 8; \ + low <<= 8; \ + } \ +} + + +inline int RangeCoder::GetCurrentCount() +{ + return (code-low)/(range /= SubRange.scale); +} + + +inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT) +{ + return (code-low)/(range >>= SHIFT); +} + + +inline void RangeCoder::Decode() +{ + low += range*SubRange.LowCount; + range *= SubRange.HighCount-SubRange.LowCount; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/coder.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/coder.hpp new file mode 100644 index 00000000..7b36ff21 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/coder.hpp @@ -0,0 +1,23 @@ +/**************************************************************************** + * Contents: 'Carryless rangecoder' by Dmitry Subbotin * + ****************************************************************************/ + + +class RangeCoder +{ + public: + void InitDecoder(Unpack *UnpackRead); + inline int GetCurrentCount(); + inline uint GetCurrentShiftCount(uint SHIFT); + inline void Decode(); + inline void PutChar(unsigned int c); + inline unsigned int GetChar(); + + uint low, code, range; + struct SUBRANGE + { + uint LowCount, HighCount, scale; + } SubRange; + + Unpack *UnpackRead; +}; diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/compress.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/compress.hpp new file mode 100644 index 00000000..73f7ee41 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/compress.hpp @@ -0,0 +1,59 @@ +#ifndef _RAR_COMPRESS_ +#define _RAR_COMPRESS_ + +// Combine pack and unpack constants to class to avoid polluting global +// namespace with numerous short names. +class PackDef +{ + public: + // Maximum LZ match length we can encode even for short distances. + static const uint MAX_LZ_MATCH = 0x1001; + + // We increment LZ match length for longer distances, because shortest + // matches are not allowed for them. Maximum length increment is 3 + // for distances larger than 256KB (0x40000). Here we define the maximum + // incremented LZ match. Normally packer does not use it, but we must be + // ready to process it in corrupt archives. + static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; + + static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + static const uint LOW_DIST_REP_COUNT = 16; + + static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC = 64; + static const uint LDC = 16; + static const uint RC = 44; + static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; + static const uint BC = 20; + + static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC30 = 60; + static const uint LDC30 = 17; + static const uint RC30 = 28; + static const uint BC30 = 20; + static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + + static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC20 = 48; + static const uint RC20 = 28; + static const uint BC20 = 19; + static const uint MC20 = 257; + + // Largest alphabet size among all values listed above. + static const uint LARGEST_TABLE_SIZE = 306; + + enum { + CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + }; +}; + + +enum FilterType { + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/consio.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/consio.cpp new file mode 100644 index 00000000..fa35d614 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/consio.cpp @@ -0,0 +1,406 @@ +#include "rar.hpp" +#include "log.cpp" + +static MESSAGE_TYPE MsgStream=MSG_STDOUT; +static RAR_CHARSET RedirectCharset=RCH_DEFAULT; +static bool ProhibitInput=false; + +const int MaxMsgSize=2*NM+2048; + +static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; + +#ifdef _WIN_ALL +static bool IsRedirected(DWORD nStdHandle) +{ + HANDLE hStd=GetStdHandle(nStdHandle); + DWORD Mode; + return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0; +} +#endif + + +void InitConsole() +{ +#ifdef _WIN_ALL + // We want messages like file names or progress percent to be printed + // immediately. Use only in Windows, in Unix they can cause wprintf %ls + // to fail with non-English strings. + setbuf(stdout,NULL); + setbuf(stderr,NULL); + + // Detect if output is redirected and set output mode properly. + // We do not want to send Unicode output to files and especially to pipes + // like '|more', which cannot handle them correctly in Windows. + // In Unix console output is UTF-8 and it is handled correctly + // when redirecting, so no need to perform any adjustments. + StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE); + StderrRedirected=IsRedirected(STD_ERROR_HANDLE); + StdinRedirected=IsRedirected(STD_INPUT_HANDLE); +#ifdef _MSC_VER + if (!StdoutRedirected) + _setmode(_fileno(stdout), _O_U16TEXT); + if (!StderrRedirected) + _setmode(_fileno(stderr), _O_U16TEXT); +#endif +#elif defined(_UNIX) + StdoutRedirected=!isatty(fileno(stdout)); + StderrRedirected=!isatty(fileno(stderr)); + StdinRedirected=!isatty(fileno(stdin)); +#endif +} + + +void SetConsoleMsgStream(MESSAGE_TYPE MsgStream) +{ + ::MsgStream=MsgStream; +} + + +void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset) +{ + ::RedirectCharset=RedirectCharset; +} + + +void ProhibitConsoleInput() +{ + ProhibitInput=true; +} + + +#ifndef SILENT +static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) +{ + // This buffer is for format string only, not for entire output, + // so it can be short enough. + wchar fmtw[1024]; + PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); +#ifdef _WIN_ALL + safebuf wchar Msg[MaxMsgSize]; + if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) + { + HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + DWORD Written; + if (RedirectCharset==RCH_UNICODE) + WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL); + else + { + // Avoid Unicode for redirect in Windows, it does not work with pipes. + safebuf char MsgA[MaxMsgSize]; + if (RedirectCharset==RCH_UTF8) + WideToUtf(Msg,MsgA,ASIZE(MsgA)); + else + WideToChar(Msg,MsgA,ASIZE(MsgA)); + if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) + CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding. + + // We already converted \n to \r\n above, so we use WriteFile instead + // of C library to avoid unnecessary additional conversion. + WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL); + } + return; + } + // MSVC2008 vfwprintf writes every character to console separately + // and it is too slow. We use direct WriteConsole call instead. + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); + DWORD Written; + WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL); +#else + vfwprintf(dest,fmtw,arglist); + // We do not use setbuf(NULL) in Unix (see comments in InitConsole). + fflush(dest); +#endif +} + + +void mprintf(const wchar *fmt,...) +{ + if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) + return; + + fflush(stderr); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout; + cvt_wprintf(dest,fmt,arglist); + va_end(arglist); +} +#endif + + +#ifndef SILENT +void eprintf(const wchar *fmt,...) +{ + if (MsgStream==MSG_NULL) + return; + + fflush(stdout); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + cvt_wprintf(stderr,fmt,arglist); + va_end(arglist); +} +#endif + + +#ifndef SILENT +static void QuitIfInputProhibited() +{ + // We cannot handle user prompts if -si is used to read file or archive data + // from stdin. + if (ProhibitInput) + { + mprintf(St(MStdinNoInput)); + ErrHandler.Exit(RARX_FATAL); + } +} + + +static void GetPasswordText(wchar *Str,uint MaxLength) +{ + if (MaxLength==0) + return; + QuitIfInputProhibited(); + if (StdinRedirected) + getwstr(Str,MaxLength); // Read from pipe or redirected file. + else + { +#ifdef _WIN_ALL + HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); + DWORD ConInMode; + GetConsoleMode(hConIn,&ConInMode); + SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. + + // We prefer ReadConsole to ReadFile, so we can read Unicode input. + DWORD Read=0; + ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); + Str[Read]=0; + SetConsoleMode(hConIn,ConInMode); + + // If entered password is longer than MAXPASSWORD and truncated, + // read its unread part anyway, so it isn't read later as the second + // password for -p switch. Low level FlushConsoleInputBuffer doesn't help + // for high level ReadConsole, which in line input mode seems to store + // the rest of string in its own internal buffer. + if (wcschr(Str,'\r')==NULL) // If '\r' is missing, the password was truncated. + while (true) + { + wchar Trail[64]; + DWORD TrailRead=0; + // Use ASIZE(Trail)-1 to reserve the space for trailing 0. + ReadConsole(hConIn,Trail,ASIZE(Trail)-1,&TrailRead,NULL); + Trail[TrailRead]=0; + if (TrailRead==0 || wcschr(Trail,'\r')!=NULL) + break; + } + +#else + char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters. +#if defined(_EMX) || defined (__VMS) + fgets(StrA,ASIZE(StrA)-1,stdin); +#elif defined(__sun) + strncpyz(StrA,getpassphrase(""),ASIZE(StrA)); +#else + strncpyz(StrA,getpass(""),ASIZE(StrA)); +#endif + CharToWide(StrA,Str,MaxLength); + cleandata(StrA,sizeof(StrA)); +#endif + } + Str[MaxLength-1]=0; + RemoveLF(Str); +} +#endif + + +#ifndef SILENT +bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + if (!StdinRedirected) + uiAlarm(UIALARM_QUESTION); + + while (true) + { +// if (!StdinRedirected) + if (Type==UIPASSWORD_GLOBAL) + eprintf(L"\n%s: ",St(MAskPsw)); + else + eprintf(St(MAskPswFor),FileName); + + wchar PlainPsw[MAXPASSWORD+1]; + GetPasswordText(PlainPsw,ASIZE(PlainPsw)); + if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) + return false; + if (wcslen(PlainPsw)>=MAXPASSWORD) + { + PlainPsw[MAXPASSWORD-1]=0; + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + } + if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) + { + eprintf(St(MReAskPsw)); + wchar CmpStr[MAXPASSWORD]; + GetPasswordText(CmpStr,ASIZE(CmpStr)); + if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0) + { + eprintf(St(MNotMatchPsw)); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(CmpStr,sizeof(CmpStr)); + continue; + } + cleandata(CmpStr,sizeof(CmpStr)); + } + Password->Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + break; + } + return true; +} +#endif + + +#ifndef SILENT +bool getwstr(wchar *str,size_t n) +{ + // Print buffered prompt title function before waiting for input. + fflush(stderr); + + QuitIfInputProhibited(); + + *str=0; +#if defined(_WIN_ALL) + // fgetws does not work well with non-English text in Windows, + // so we do not use it. + if (StdinRedirected) // ReadConsole does not work if redirected. + { + // fgets does not work well with pipes in Windows in our test. + // Let's use files. + Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + SrcFile.SetLineInputMode(true); + int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); + if (ReadSize<=0) + { + // Looks like stdin is a null device. We can enter to infinite loop + // calling Ask(), so let's better exit. + ErrHandler.Exit(RARX_USERBREAK); + } + StrA[ReadSize]=0; + + // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR, + // such as send passwords, we get OEM encoding by default, unless we + // use "chcp" in console. But we avoid OEM to ANSI conversion, + // because we also want to handle ANSI files redirection correctly, + // like "rar ... < ansifile.txt". + CharToWide(&StrA[0],str,n); + cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords. + } + else + { + DWORD ReadSize=0; + if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0) + return false; + str[ReadSize]=0; + } +#else + if (fgetws(str,n,stdin)==NULL) + ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop. +#endif + RemoveLF(str); + return true; +} +#endif + + +#ifndef SILENT +// We allow this function to return 0 in case of invalid input, +// because it might be convenient to press Enter to some not dangerous +// prompts like "insert disk with next volume". We should call this function +// again in case of 0 in dangerous prompt such as overwriting file. +int Ask(const wchar *AskStr) +{ + uiAlarm(UIALARM_QUESTION); + + const int MaxItems=10; + wchar Item[MaxItems][40]; + int ItemKeyPos[MaxItems],NumItems=0; + + for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_')) + { + wchar *CurItem=Item[NumItems]; + wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); + wchar *EndItem=wcschr(CurItem,'_'); + if (EndItem!=NULL) + *EndItem=0; + int KeyPos=0,CurKey; + while ((CurKey=CurItem[KeyPos])!=0) + { + bool Found=false; + for (int I=0;I<NumItems && !Found;I++) + if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey)) + Found=true; + if (!Found && CurKey!=' ') + break; + KeyPos++; + } + ItemKeyPos[NumItems]=KeyPos; + NumItems++; + } + + for (int I=0;I<NumItems;I++) + { + eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", "); + int KeyPos=ItemKeyPos[I]; + for (int J=0;J<KeyPos;J++) + eprintf(L"%c",Item[I][J]); + eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]); + } + eprintf(L" "); + wchar Str[50]; + getwstr(Str,ASIZE(Str)); + wchar Ch=toupperw(Str[0]); + for (int I=0;I<NumItems;I++) + if (Ch==Item[I][ItemKeyPos[I]]) + return I+1; + return 0; +} +#endif + + +static bool IsCommentUnsafe(const wchar *Data,size_t Size) +{ + for (size_t I=0;I<Size;I++) + if (Data[I]==27 && Data[I+1]=='[') + for (size_t J=I+2;J<Size;J++) + { + // Return true for <ESC>[{key};"{string}"p used to redefine + // a keyboard key on some terminals. + if (Data[J]=='\"') + return true; + if (!IsDigit(Data[J]) && Data[J]!=';') + break; + } + return false; +} + + +void OutComment(const wchar *Comment,size_t Size) +{ + if (IsCommentUnsafe(Comment,Size)) + return; + const size_t MaxOutSize=0x400; + for (size_t I=0;I<Size;I+=MaxOutSize) + { + wchar Msg[MaxOutSize+1]; + size_t CopySize=Min(MaxOutSize,Size-I); + wcsncpy(Msg,Comment+I,CopySize); + Msg[CopySize]=0; + mprintf(L"%s",Msg); + } + mprintf(L"\n"); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/consio.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/consio.hpp new file mode 100644 index 00000000..bf97289e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/consio.hpp @@ -0,0 +1,28 @@ +#ifndef _RAR_CONSIO_ +#define _RAR_CONSIO_ + +void InitConsole(); +void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); +void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); +void ProhibitConsoleInput(); +void OutComment(const wchar *Comment,size_t Size); + +#ifndef SILENT +bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); +#endif + +#ifdef SILENT + inline void mprintf(const wchar *fmt,...) {} + inline void eprintf(const wchar *fmt,...) {} + inline void Alarm() {} + inline int Ask(const wchar *AskStr) {return 0;} + inline bool getwstr(wchar *str,size_t n) {return false;} +#else + void mprintf(const wchar *fmt,...); + void eprintf(const wchar *fmt,...); + void Alarm(); + int Ask(const wchar *AskStr); + bool getwstr(wchar *str,size_t n); +#endif + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crc.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crc.cpp new file mode 100644 index 00000000..0e5e1320 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crc.cpp @@ -0,0 +1,112 @@ +// This CRC function is based on Intel Slicing-by-8 algorithm. +// +// Original Intel Slicing-by-8 code is available here: +// +// http://sourceforge.net/projects/slicing-by-8/ +// +// Original Intel Slicing-by-8 code is licensed as: +// +// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved +// +// This software program is licensed subject to the BSD License, +// available at http://www.opensource.org/licenses/bsd-license.html + + +#include "rar.hpp" + +#ifndef SFX_MODULE +// User suggested to avoid BSD license in SFX module, so they do not need +// to include the license to SFX archive. +#define USE_SLICING +#endif + +static uint crc_tables[8][256]; // Tables for Slicing-by-8. + + +// Build the classic CRC32 lookup table. +// We also provide this function to legacy RAR and ZIP decryption code. +void InitCRC32(uint *CRCTab) +{ + if (CRCTab[1]!=0) + return; + for (uint I=0;I<256;I++) + { + uint C=I; + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1); + CRCTab[I]=C; + } +} + + +static void InitTables() +{ + InitCRC32(crc_tables[0]); + +#ifdef USE_SLICING + for (uint I=0;I<256;I++) // Build additional lookup tables. + { + uint C=crc_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc_tables[0][(byte)C]^(C>>8); + crc_tables[J][I]=C; + } + } +#endif +} + + +struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; + +uint CRC32(uint StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + +#ifdef USE_SLICING + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7);Size--,Data++) + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (;Size>=8;Size-=8,Data+=8) + { +#ifdef BIG_ENDIAN + StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); + uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); +#else + StartCRC ^= *(uint32 *) Data; + uint NextData = *(uint32 *) (Data+4); +#endif + StartCRC = crc_tables[7][(byte) StartCRC ] ^ + crc_tables[6][(byte)(StartCRC >> 8) ] ^ + crc_tables[5][(byte)(StartCRC >> 16)] ^ + crc_tables[4][(byte)(StartCRC >> 24)] ^ + crc_tables[3][(byte) NextData ] ^ + crc_tables[2][(byte)(NextData >> 8) ] ^ + crc_tables[1][(byte)(NextData >> 16)] ^ + crc_tables[0][(byte)(NextData >> 24)]; + } +#endif + + for (;Size>0;Size--,Data++) // Process left data. + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + +#ifndef SFX_MODULE +// For RAR 1.4 archives in case somebody still has them. +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + for (size_t I=0;I<Size;I++) + { + StartCRC=(StartCRC+Data[I])&0xffff; + StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff; + } + return StartCRC; +} +#endif + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crc.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crc.hpp new file mode 100644 index 00000000..d8fea281 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crc.hpp @@ -0,0 +1,15 @@ +#ifndef _RAR_CRC_ +#define _RAR_CRC_ + +// This function is only to intialize external CRC tables. We do not need to +// call it before calculating CRC32. +void InitCRC32(uint *CRCTab); + +uint CRC32(uint StartCRC,const void *Addr,size_t Size); + +#ifndef SFX_MODULE +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size); +#endif + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.cpp new file mode 100644 index 00000000..fc2126d4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.cpp @@ -0,0 +1,134 @@ +#include "rar.hpp" + +#ifndef SFX_MODULE +#include "crypt1.cpp" +#include "crypt2.cpp" +#endif +#include "crypt3.cpp" +#include "crypt5.cpp" + + +CryptData::CryptData() +{ + Method=CRYPT_NONE; + memset(KDF3Cache,0,sizeof(KDF3Cache)); + memset(KDF5Cache,0,sizeof(KDF5Cache)); + KDF3CachePos=0; + KDF5CachePos=0; + memset(CRCTab,0,sizeof(CRCTab)); +} + + +CryptData::~CryptData() +{ + cleandata(KDF3Cache,sizeof(KDF3Cache)); + cleandata(KDF5Cache,sizeof(KDF5Cache)); +} + + + + +void CryptData::DecryptBlock(byte *Buf,size_t Size) +{ + switch(Method) + { +#ifndef SFX_MODULE + case CRYPT_RAR13: + Decrypt13(Buf,Size); + break; + case CRYPT_RAR15: + Crypt15(Buf,Size); + break; + case CRYPT_RAR20: + for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE) + DecryptBlock20(Buf+I); + break; +#endif + case CRYPT_RAR30: + case CRYPT_RAR50: + rin.blockDecrypt(Buf,Size,Buf); + break; + } +} + + +bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, + SecPassword *Password,const byte *Salt, + const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck) +{ + if (!Password->IsSet() || Method==CRYPT_NONE) + return false; + + CryptData::Method=Method; + + wchar PwdW[MAXPASSWORD]; + Password->Get(PwdW,ASIZE(PwdW)); + char PwdA[MAXPASSWORD]; + WideToChar(PwdW,PwdA,ASIZE(PwdA)); + + switch(Method) + { +#ifndef SFX_MODULE + case CRYPT_RAR13: + SetKey13(PwdA); + break; + case CRYPT_RAR15: + SetKey15(PwdA); + break; + case CRYPT_RAR20: + SetKey20(PwdA); + break; +#endif + case CRYPT_RAR30: + SetKey30(Encrypt,Password,PwdW,Salt); + break; + case CRYPT_RAR50: + SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + break; + } + cleandata(PwdA,sizeof(PwdA)); + cleandata(PwdW,sizeof(PwdW)); + return true; +} + + +// Use the current system time to additionally randomize data. +static void TimeRandomize(byte *RndBuf,size_t BufSize) +{ + static uint Count=0; + RarTime CurTime; + CurTime.SetCurrentTime(); + uint64 Random=CurTime.GetWin()+clock(); + for (size_t I=0;I<BufSize;I++) + { + byte RndByte = byte (Random >> ( (I & 7) * 8 )); + RndBuf[I]=byte( (RndByte ^ I) + Count++); + } +} + + + + +// Fill buffer with random data. +void GetRnd(byte *RndBuf,size_t BufSize) +{ + bool Success=false; +#if defined(_WIN_ALL) + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; + CryptReleaseContext(hProvider, 0); + } +#elif defined(_UNIX) + FILE *rndf = fopen("/dev/urandom", "r"); + if (rndf!=NULL) + { + Success=fread(RndBuf, BufSize, 1, rndf) == BufSize; + fclose(rndf); + } +#endif + // We use this code only as the last resort if code above failed. + if (!Success) + TimeRandomize(RndBuf,BufSize); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.hpp new file mode 100644 index 00000000..f6382ef5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt.hpp @@ -0,0 +1,101 @@ +#ifndef _RAR_CRYPT_ +#define _RAR_CRYPT_ + + +enum CRYPT_METHOD { + CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50 +}; + +#define SIZE_SALT50 16 +#define SIZE_SALT30 8 +#define SIZE_INITV 16 +#define SIZE_PSWCHECK 8 +#define SIZE_PSWCHECK_CSUM 4 + +#define CRYPT_BLOCK_SIZE 16 +#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf + +#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. +#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. +#define CRYPT_VERSION 0 // Supported encryption version. + + +class CryptData +{ + struct KDF5CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT50]; + byte Key[32]; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte PswCheckValue[SHA256_DIGEST_SIZE]; + byte HashKeyValue[SHA256_DIGEST_SIZE]; + }; + + struct KDF3CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT30]; + byte Key[16]; + byte Init[16]; + bool SaltPresent; + }; + + + private: + void SetKey13(const char *Password); + void Decrypt13(byte *Data,size_t Count); + + void SetKey15(const char *Password); + void Crypt15(byte *Data,size_t Count); + + void SetKey20(const char *Password); + void Swap20(byte *Ch1,byte *Ch2); + void UpdKeys20(byte *Buf); + void EncryptBlock20(byte *Buf); + void DecryptBlock20(byte *Buf); + + void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); + void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); + + KDF3CacheItem KDF3Cache[4]; + uint KDF3CachePos; + + KDF5CacheItem KDF5Cache[4]; + uint KDF5CachePos; + + CRYPT_METHOD Method; + + Rijndael rin; + + uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption. + + byte SubstTable20[256]; + uint Key20[4]; + + byte Key13[3]; + ushort Key15[4]; + public: + CryptData(); + ~CryptData(); + bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt, + byte *HashKey,byte *PswCheck); + void SetAV15Encryption(); + void SetCmt13Encryption(); + void EncryptBlock(byte *Buf,size_t Size); + void DecryptBlock(byte *Buf,size_t Size); + static void SetSalt(byte *Salt,size_t SaltSize); +}; + +void GetRnd(byte *RndBuf,size_t BufSize); + +void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest); +void pbkdf2(const byte *pass, size_t pass_len, const byte *salt, + size_t salt_len,byte *key, byte *Value1, byte *Value2, + uint rounds); + +void ConvertHashToMAC(HashValue *Value,byte *Key); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt1.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt1.cpp new file mode 100644 index 00000000..14263937 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt1.cpp @@ -0,0 +1,79 @@ +extern uint CRCTab[256]; + +void CryptData::SetKey13(const char *Password) +{ + Key13[0]=Key13[1]=Key13[2]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key13[0]+=P; + Key13[1]^=P; + Key13[2]+=P; + Key13[2]=(byte)rotls(Key13[2],1,8); + } +} + + +void CryptData::SetKey15(const char *Password) +{ + InitCRC32(CRCTab); + uint PswCRC=CRC32(0xffffffff,Password,strlen(Password)); + Key15[0]=PswCRC&0xffff; + Key15[1]=(PswCRC>>16)&0xffff; + Key15[2]=Key15[3]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key15[2]^=P^CRCTab[P]; + Key15[3]+=P+(CRCTab[P]>>16); + } +} + + +void CryptData::SetAV15Encryption() +{ + InitCRC32(CRCTab); + Method=CRYPT_RAR15; + Key15[0]=0x4765; + Key15[1]=0x9021; + Key15[2]=0x7382; + Key15[3]=0x5215; +} + + +void CryptData::SetCmt13Encryption() +{ + Method=CRYPT_RAR13; + Key13[0]=0; + Key13[1]=7; + Key13[2]=77; +} + + +void CryptData::Decrypt13(byte *Data,size_t Count) +{ + while (Count--) + { + Key13[1]+=Key13[2]; + Key13[0]+=Key13[1]; + *Data-=Key13[0]; + Data++; + } +} + + +void CryptData::Crypt15(byte *Data,size_t Count) +{ + while (Count--) + { + Key15[0]+=0x1234; + Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; + Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; + Key15[0]^=Key15[2]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16); + Key15[0]^=Key15[3]; + *Data^=(byte)(Key15[0]>>8); + Data++; + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt2.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt2.cpp new file mode 100644 index 00000000..5fa4a97d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt2.cpp @@ -0,0 +1,133 @@ +#define NROUNDS 32 + +#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \ + ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \ + ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \ + ((uint)SubstTable20[(int)(t>>24)&255]<<24) ) + + +static byte InitSubstTable20[256]={ + 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, + 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, + 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, + 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, + 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, + 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, + 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, + 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, + 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, + 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, + 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, + 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, + 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, + 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, + 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, + 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 +}; + + +void CryptData::SetKey20(const char *Password) +{ + InitCRC32(CRCTab); + + char Psw[MAXPASSWORD]; + strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below. + size_t PswLength=strlen(Psw); + + Key20[0]=0xD3A3B879L; + Key20[1]=0x3F6D12F7L; + Key20[2]=0x7515A235L; + Key20[3]=0xA4E7F123L; + + memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20)); + for (uint J=0;J<256;J++) + for (size_t I=0;I<PswLength;I+=2) + { + uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff]; + uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff]; + for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++) + Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]); + } + + // Incomplete last block of password must be zero padded. + if ((PswLength & CRYPT_BLOCK_MASK)!=0) + for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++) + Psw[I]=0; + + for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE) + EncryptBlock20((byte *)Psw+I); +} + + +void CryptData::EncryptBlock20(byte *Buf) +{ + uint A,B,C,D,T,TA,TB; + A=RawGet4(Buf+0)^Key20[0]; + B=RawGet4(Buf+4)^Key20[1]; + C=RawGet4(Buf+8)^Key20[2]; + D=RawGet4(Buf+12)^Key20[3]; + for(int I=0;I<NROUNDS;I++) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(Buf); +} + + +void CryptData::DecryptBlock20(byte *Buf) +{ + byte InBuf[16]; + uint A,B,C,D,T,TA,TB; + A=RawGet4(Buf+0)^Key20[0]; + B=RawGet4(Buf+4)^Key20[1]; + C=RawGet4(Buf+8)^Key20[2]; + D=RawGet4(Buf+12)^Key20[3]; + memcpy(InBuf,Buf,sizeof(InBuf)); + for(int I=NROUNDS-1;I>=0;I--) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(InBuf); +} + + +void CryptData::UpdKeys20(byte *Buf) +{ + for (int I=0;I<16;I+=4) + { + Key20[0]^=CRCTab[Buf[I]]; + Key20[1]^=CRCTab[Buf[I+1]]; + Key20[2]^=CRCTab[Buf[I+2]]; + Key20[3]^=CRCTab[Buf[I+3]]; + } +} + + +void CryptData::Swap20(byte *Ch1,byte *Ch2) +{ + byte Ch=*Ch1; + *Ch1=*Ch2; + *Ch2=Ch; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt3.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt3.cpp new file mode 100644 index 00000000..fe3bf97b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt3.cpp @@ -0,0 +1,68 @@ +void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt) +{ + byte AESKey[16],AESInit[16]; + + bool Cached=false; + for (uint I=0;I<ASIZE(KDF3Cache);I++) + if (KDF3Cache[I].Pwd==*Password && + (Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL && + KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0)) + { + memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey)); + SecHideData(AESKey,sizeof(AESKey),false,false); + memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit)); + Cached=true; + break; + } + + if (!Cached) + { + byte RawPsw[2*MAXPASSWORD+SIZE_SALT30]; + WideToRaw(PwdW,RawPsw,ASIZE(RawPsw)); + size_t RawLength=2*wcslen(PwdW); + if (Salt!=NULL) + { + memcpy(RawPsw+RawLength,Salt,SIZE_SALT30); + RawLength+=SIZE_SALT30; + } + sha1_context c; + sha1_init(&c); + + const uint HashRounds=0x40000; + for (uint I=0;I<HashRounds;I++) + { + sha1_process_rar29( &c, RawPsw, RawLength ); + byte PswNum[3]; + PswNum[0]=(byte)I; + PswNum[1]=(byte)(I>>8); + PswNum[2]=(byte)(I>>16); + sha1_process(&c, PswNum, 3); + if (I%(HashRounds/16)==0) + { + sha1_context tempc=c; + uint32 digest[5]; + sha1_done( &tempc, digest ); + AESInit[I/(HashRounds/16)]=(byte)digest[4]; + } + } + uint32 digest[5]; + sha1_done( &c, digest ); + for (uint I=0;I<4;I++) + for (uint J=0;J<4;J++) + AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); + + KDF3Cache[KDF3CachePos].Pwd=*Password; + if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true) + memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30); + memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey)); + SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false); + memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit)); + KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache); + + cleandata(RawPsw,sizeof(RawPsw)); + } + rin.Init(Encrypt, AESKey, 128, AESInit); + cleandata(AESKey,sizeof(AESKey)); + cleandata(AESInit,sizeof(AESInit)); +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/crypt5.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt5.cpp new file mode 100644 index 00000000..7562469f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/crypt5.cpp @@ -0,0 +1,233 @@ +static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest, + sha256_context *ICtxOpt,bool *SetIOpt, + sha256_context *RCtxOpt,bool *SetROpt) +{ + const size_t Sha256BlockSize=64; // As defined in RFC 4868. + + byte KeyHash[SHA256_DIGEST_SIZE]; + if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash. + { + sha256_context KCtx; + sha256_init(&KCtx); + sha256_process(&KCtx, Key, KeyLength); + sha256_done(&KCtx, KeyHash); + + Key = KeyHash; + KeyLength = SHA256_DIGEST_SIZE; + } + + byte KeyBuf[Sha256BlockSize]; // Store the padded key here. + sha256_context ICtx; + + if (ICtxOpt!=NULL && *SetIOpt) + ICtx=*ICtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest. + KeyBuf[I] = Key[I] ^ 0x36; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x36; + + sha256_init(&ICtx); + sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse. + { + *ICtxOpt=ICtx; + *SetIOpt=true; + } + + sha256_process(&ICtx, Data, DataLength); // Hash data. + + byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data. + sha256_done(&ICtx, IDig); + + sha256_context RCtx; + + if (RCtxOpt!=NULL && *SetROpt) + RCtx=*RCtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding. + KeyBuf[I] = Key[I] ^ 0x5c; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x5c; + + sha256_init(&RCtx); + sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse. + { + *RCtxOpt=RCtx; + *SetROpt=true; + } + + sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest. + + sha256_done(&RCtx, ResDigest); +} + + +// PBKDF2 for 32 byte key length. We generate the key for specified number +// of iteration count also as two supplementary values (key for checksums +// and password verification) for iterations+16 and iterations+32. +void pbkdf2(const byte *Pwd, size_t PwdLength, + const byte *Salt, size_t SaltLength, + byte *Key, byte *V1, byte *V2, uint Count) +{ + const size_t MaxSalt=64; + byte SaltData[MaxSalt+4]; + memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); + + SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. + SaltData[SaltLength + 1] = 0; + SaltData[SaltLength + 2] = 0; + SaltData[SaltLength + 3] = 1; + + // First iteration: HMAC of password, salt and block index (1). + byte U1[SHA256_DIGEST_SIZE]; + hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL); + byte Fn[SHA256_DIGEST_SIZE]; // Current function value. + memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration. + + uint CurCount[] = { Count-1, 16, 16 }; + byte *CurValue[] = { Key , V1, V2 }; + + sha256_context ICtxOpt,RCtxOpt; + bool SetIOpt=false,SetROpt=false; + + byte U2[SHA256_DIGEST_SIZE]; + for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values. + { + for (uint J = 0; J < CurCount[I]; J++) + { + // U2 = PRF (P, U1). + hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt); + memcpy(U1, U2, sizeof(U1)); + for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U. + Fn[K] ^= U1[K]; + } + memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE); + } + + cleandata(SaltData, sizeof(SaltData)); + cleandata(Fn, sizeof(Fn)); + cleandata(U1, sizeof(U1)); + cleandata(U2, sizeof(U2)); +} + + +void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, + byte *PswCheck) +{ + if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) + return; + + byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; + bool Found=false; + for (uint I=0;I<ASIZE(KDF5Cache);I++) + { + KDF5CacheItem *Item=KDF5Cache+I; + if (Item->Lg2Count==Lg2Cnt && Item->Pwd==*Password && + memcmp(Item->Salt,Salt,SIZE_SALT50)==0) + { + memcpy(Key,Item->Key,sizeof(Key)); + SecHideData(Key,sizeof(Key),false,false); + + memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue)); + memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue)); + Found=true; + break; + } + } + + if (!Found) + { + char PwdUtf[MAXPASSWORD*4]; + WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf)); + + pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt)); + cleandata(PwdUtf,sizeof(PwdUtf)); + + KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache)); + Item->Lg2Count=Lg2Cnt; + Item->Pwd=*Password; + memcpy(Item->Salt,Salt,SIZE_SALT50); + memcpy(Item->Key,Key,sizeof(Item->Key)); + memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue)); + memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue)); + SecHideData(Item->Key,sizeof(Item->Key),true,false); + } + if (HashKey!=NULL) + memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE); + if (PswCheck!=NULL) + { + memset(PswCheck,0,SIZE_PSWCHECK); + for (uint I=0;I<SHA256_DIGEST_SIZE;I++) + PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I]; + cleandata(PswCheckValue,sizeof(PswCheckValue)); + } + + // NULL initialization vector is possible if we only need the password + // check value for archive encryption header. + if (InitV!=NULL) + rin.Init(Encrypt, Key, 256, InitV); + + cleandata(Key,sizeof(Key)); +} + + +void ConvertHashToMAC(HashValue *Value,byte *Key) +{ + if (Value->Type==HASH_CRC32) + { + byte RawCRC[4]; + RawPut4(Value->CRC32,RawCRC); + byte Digest[SHA256_DIGEST_SIZE]; + hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL); + Value->CRC32=0; + for (uint I=0;I<ASIZE(Digest);I++) + Value->CRC32^=Digest[I] << ((I & 3) * 8); + } + if (Value->Type==HASH_BLAKE2) + { + byte Digest[BLAKE2_DIGEST_SIZE]; + hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL); + memcpy(Value->Digest,Digest,sizeof(Value->Digest)); + } +} + + +#if 0 +static void TestPBKDF2(); +struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF; + +void TestPBKDF2() // Test PBKDF2 HMAC-SHA256 +{ + byte Key[32],V1[32],V2[32]; + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1); + byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b }; + mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096); + byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a }; + mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536); + byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf}; + mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed"); +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/dll.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/dll.cpp new file mode 100644 index 00000000..31818e49 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/dll.cpp @@ -0,0 +1,496 @@ +#include "rar.hpp" + +static int RarErrorToDll(RAR_EXIT ErrCode); + +struct DataSet +{ + CommandData Cmd; + Archive Arc; + CmdExtract Extract; + int OpenMode; + int HeaderSize; + + DataSet():Arc(&Cmd),Extract(&Cmd) {}; +}; + + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) +{ + RAROpenArchiveDataEx rx; + memset(&rx,0,sizeof(rx)); + rx.ArcName=r->ArcName; + rx.OpenMode=r->OpenMode; + rx.CmtBuf=r->CmtBuf; + rx.CmtBufSize=r->CmtBufSize; + HANDLE hArc=RAROpenArchiveEx(&rx); + r->OpenResult=rx.OpenResult; + r->CmtSize=rx.CmtSize; + r->CmtState=rx.CmtState; + return hArc; +} + + +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) +{ + DataSet *Data=NULL; + try + { + ErrHandler.Clean(); + + r->OpenResult=0; + Data=new DataSet; + Data->Cmd.DllError=0; + Data->OpenMode=r->OpenMode; + Data->Cmd.FileArgs.AddString(L"*"); + Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0; + + char AnsiArcName[NM]; + *AnsiArcName=0; + if (r->ArcName!=NULL) + { + strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); +#ifdef _WIN_ALL + if (!AreFileApisANSI()) + { + OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); + AnsiArcName[ASIZE(AnsiArcName)-1]=0; + } +#endif + } + + wchar ArcName[NM]; + GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); + + Data->Cmd.AddArcName(ArcName); + Data->Cmd.Overwrite=OVERWRITE_ALL; + Data->Cmd.VersionControl=1; + + Data->Cmd.Callback=r->Callback; + Data->Cmd.UserData=r->UserData; + + // Open shared mode is added by request of dll users, who need to + // browse and unpack archives while downloading. + Data->Cmd.OpenShared = true; + if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) + { + r->OpenResult=ERAR_EOPEN; + delete Data; + return NULL; + } + if (!Data->Arc.IsArchive(true)) + { + if (Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + { + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) + r->OpenResult=RarErrorToDll(ErrCode); + else + r->OpenResult=ERAR_BAD_ARCHIVE; + } + delete Data; + return NULL; + } + r->Flags=0; + + if (Data->Arc.Volume) + r->Flags|=ROADF_VOLUME; + if (Data->Arc.MainComment) + r->Flags|=ROADF_COMMENT; + if (Data->Arc.Locked) + r->Flags|=ROADF_LOCK; + if (Data->Arc.Solid) + r->Flags|=ROADF_SOLID; + if (Data->Arc.NewNumbering) + r->Flags|=ROADF_NEWNUMBERING; + if (Data->Arc.Signed) + r->Flags|=ROADF_SIGNED; + if (Data->Arc.Protected) + r->Flags|=ROADF_RECOVERY; + if (Data->Arc.Encrypted) + r->Flags|=ROADF_ENCHEADERS; + if (Data->Arc.FirstVolume) + r->Flags|=ROADF_FIRSTVOLUME; + + Array<wchar> CmtDataW; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) + { + if (r->CmtBufW!=NULL) + { + CmtDataW.Push(0); + size_t Size=wcslen(&CmtDataW[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW)); + r->CmtBufW[r->CmtSize-1]=0; + } + else + if (r->CmtBuf!=NULL) + { + Array<char> CmtData(CmtDataW.Size()*4+1); + memset(&CmtData[0],0,CmtData.Size()); + WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); + size_t Size=strlen(&CmtData[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + r->CmtBuf[r->CmtSize-1]=0; + } + } + else + r->CmtState=r->CmtSize=0; + Data->Extract.ExtractArchiveInit(Data->Arc); + return (HANDLE)Data; + } + catch (RAR_EXIT ErrCode) + { + if (Data!=NULL && Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + r->OpenResult=RarErrorToDll(ErrCode); + if (Data != NULL) + delete Data; + return NULL; + } + catch (std::bad_alloc&) // Catch 'new' exception. + { + r->OpenResult=ERAR_NO_MEMORY; + if (Data != NULL) + delete Data; + } + return NULL; // To make compilers happy. +} + + +int PASCAL RARCloseArchive(HANDLE hArcData) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + bool Success=Data==NULL ? false:Data->Arc.Close(); + delete Data; + return Success ? ERAR_SUCCESS : ERAR_ECLOSE; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } +} + + +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) +{ + struct RARHeaderDataEx X; + memset(&X,0,sizeof(X)); + + int Code=RARReadHeaderEx(hArcData,&X); + + strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); + strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); + D->Flags=X.Flags; + D->PackSize=X.PackSize; + D->UnpSize=X.UnpSize; + D->HostOS=X.HostOS; + D->FileCRC=X.FileCRC; + D->FileTime=X.FileTime; + D->UnpVer=X.UnpVer; + D->Method=X.Method; + D->FileAttr=X.FileAttr; + D->CmtSize=0; + D->CmtState=0; + + return Code; +} + + +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) + { + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && + Data->Arc.EndArcHead.NextVolume) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return RARReadHeaderEx(hArcData,D); + } + else + return ERAR_EOPEN; + + if (Data->Arc.BrokenHeader) + return ERAR_BAD_DATA; + + // Might be necessary if RARSetPassword is still called instead of + // open callback for RAR5 archives and if password is invalid. + if (Data->Arc.FailedHeaderDecryption) + return ERAR_BAD_PASSWORD; + + return ERAR_END_ARCHIVE; + } + FileHeader *hd=&Data->Arc.FileHead; + if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) + { + int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); + if (Code==0) + return RARReadHeaderEx(hArcData,D); + else + return Code; + } + wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); + WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); + + wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); + WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); +#ifdef _WIN_ALL + CharToOemA(D->FileName,D->FileName); +#endif + + D->Flags=0; + if (hd->SplitBefore) + D->Flags|=RHDF_SPLITBEFORE; + if (hd->SplitAfter) + D->Flags|=RHDF_SPLITAFTER; + if (hd->Encrypted) + D->Flags|=RHDF_ENCRYPTED; + if (hd->Solid) + D->Flags|=RHDF_SOLID; + if (hd->Dir) + D->Flags|=RHDF_DIRECTORY; + + D->PackSize=uint(hd->PackSize & 0xffffffff); + D->PackSizeHigh=uint(hd->PackSize>>32); + D->UnpSize=uint(hd->UnpSize & 0xffffffff); + D->UnpSizeHigh=uint(hd->UnpSize>>32); + D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; + D->UnpVer=Data->Arc.FileHead.UnpVer; + D->FileCRC=hd->FileHash.CRC32; + D->FileTime=hd->mtime.GetDos(); + + uint64 MRaw=hd->mtime.GetWin(); + D->MtimeLow=(uint)MRaw; + D->MtimeHigh=(uint)(MRaw>>32); + uint64 CRaw=hd->ctime.GetWin(); + D->CtimeLow=(uint)CRaw; + D->CtimeHigh=(uint)(CRaw>>32); + uint64 ARaw=hd->atime.GetWin(); + D->AtimeLow=(uint)ARaw; + D->AtimeHigh=(uint)(ARaw>>32); + + D->Method=hd->Method+0x30; + D->FileAttr=hd->FileAttr; + D->CmtSize=0; + D->CmtState=0; + + D->DictSize=uint(hd->WinSize/1024); + + switch (hd->FileHash.Type) + { + case HASH_RAR14: + case HASH_CRC32: + D->HashType=RAR_HASH_CRC32; + break; + case HASH_BLAKE2: + D->HashType=RAR_HASH_BLAKE2; + memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + break; + default: + D->HashType=RAR_HASH_NONE; + break; + } + + D->RedirType=hd->RedirType; + // RedirNameSize sanity check is useful in case some developer + // did not initialize Reserved area with 0 as required in docs. + // We have taken 'Redir*' fields from Reserved area. We may remove + // this RedirNameSize check sometimes later. + if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && + D->RedirNameSize>0 && D->RedirNameSize<100000) + wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); + D->DirTarget=hd->DirTarget; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return ERAR_SUCCESS; +} + + +int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + Data->Cmd.DllError=0; + if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || + Operation==RAR_SKIP && !Data->Arc.Solid) + { + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && + Data->Arc.FileHead.SplitAfter) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return ERAR_SUCCESS; + } + else + return ERAR_EOPEN; + Data->Arc.SeekToNext(); + } + else + { + Data->Cmd.DllOpMode=Operation; + + *Data->Cmd.ExtrPath=0; + *Data->Cmd.DllDestName=0; + + if (DestPath!=NULL) + { + char ExtrPathA[NM]; + strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestPath, + // because we do not know DestPath length and OemToCharBuffA + // does not stop at 0. + OemToCharA(ExtrPathA,ExtrPathA); +#endif + CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + } + if (DestName!=NULL) + { + char DestNameA[NM]; + strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestName, + // because we do not know DestName length and OemToCharBuffA + // does not stop at 0. + OemToCharA(DestNameA,DestNameA); +#endif + CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); + } + + if (DestPathW!=NULL) + { + wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + } + + if (DestNameW!=NULL) + wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); + + wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command)); + Data->Cmd.Test=Operation!=RAR_EXTRACT; + bool Repeat=false; + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); + + // Now we process extra file information if any. + // + // Archive can be closed if we process volumes, next volume is missing + // and current one is already removed or deleted. So we need to check + // if archive is still open to avoid calling file operations on + // the invalid file handle. Some of our file operations like Seek() + // process such invalid handle correctly, some not. + while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && + Data->Arc.GetHeaderType()==HEAD_SERVICE) + { + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); + Data->Arc.SeekToNext(); + } + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + } + } + catch (std::bad_alloc&) + { + return ERAR_NO_MEMORY; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return Data->Cmd.DllError; +} + + +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) +{ + return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL); +} + + +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) +{ + return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName); +} + + +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ChangeVolProc=ChangeVolProc; +} + + +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.Callback=Callback; + Data->Cmd.UserData=UserData; +} + + +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ProcessDataProc=ProcessDataProc; +} + + +void PASCAL RARSetPassword(HANDLE hArcData,char *Password) +{ +#ifndef RAR_NOCRYPT + DataSet *Data=(DataSet *)hArcData; + wchar PasswordW[MAXPASSWORD]; + GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + Data->Cmd.Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); +#endif +} + + +int PASCAL RARGetDllVersion() +{ + return RAR_DLL_VERSION; +} + + +static int RarErrorToDll(RAR_EXIT ErrCode) +{ + switch(ErrCode) + { + case RARX_FATAL: + case RARX_READ: + return ERAR_EREAD; + case RARX_CRC: + return ERAR_BAD_DATA; + case RARX_WRITE: + return ERAR_EWRITE; + case RARX_OPEN: + return ERAR_EOPEN; + case RARX_CREATE: + return ERAR_ECREATE; + case RARX_MEMORY: + return ERAR_NO_MEMORY; + case RARX_BADPWD: + return ERAR_BAD_PASSWORD; + case RARX_SUCCESS: + return ERAR_SUCCESS; // 0. + default: + return ERAR_UNKNOWN; + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/dll.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/dll.hpp new file mode 100644 index 00000000..c785ff18 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/dll.hpp @@ -0,0 +1,189 @@ +#ifndef _UNRAR_DLL_ +#define _UNRAR_DLL_ + +#pragma pack(push, 1) + +#define ERAR_SUCCESS 0 +#define ERAR_END_ARCHIVE 10 +#define ERAR_NO_MEMORY 11 +#define ERAR_BAD_DATA 12 +#define ERAR_BAD_ARCHIVE 13 +#define ERAR_UNKNOWN_FORMAT 14 +#define ERAR_EOPEN 15 +#define ERAR_ECREATE 16 +#define ERAR_ECLOSE 17 +#define ERAR_EREAD 18 +#define ERAR_EWRITE 19 +#define ERAR_SMALL_BUF 20 +#define ERAR_UNKNOWN 21 +#define ERAR_MISSING_PASSWORD 22 +#define ERAR_EREFERENCE 23 +#define ERAR_BAD_PASSWORD 24 + +#define RAR_OM_LIST 0 +#define RAR_OM_EXTRACT 1 +#define RAR_OM_LIST_INCSPLIT 2 + +#define RAR_SKIP 0 +#define RAR_TEST 1 +#define RAR_EXTRACT 2 + +#define RAR_VOL_ASK 0 +#define RAR_VOL_NOTIFY 1 + +#define RAR_DLL_VERSION 8 + +#define RAR_HASH_NONE 0 +#define RAR_HASH_CRC32 1 +#define RAR_HASH_BLAKE2 2 + + +#ifdef _UNIX +#define CALLBACK +#define PASCAL +#define LONG long +#define HANDLE void * +#define LPARAM long +#define UINT unsigned int +#endif + +#define RHDF_SPLITBEFORE 0x01 +#define RHDF_SPLITAFTER 0x02 +#define RHDF_ENCRYPTED 0x04 +#define RHDF_SOLID 0x10 +#define RHDF_DIRECTORY 0x20 + + +struct RARHeaderData +{ + char ArcName[260]; + char FileName[260]; + unsigned int Flags; + unsigned int PackSize; + unsigned int UnpSize; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + + +struct RARHeaderDataEx +{ + char ArcName[1024]; + wchar_t ArcNameW[1024]; + char FileName[1024]; + wchar_t FileNameW[1024]; + unsigned int Flags; + unsigned int PackSize; + unsigned int PackSizeHigh; + unsigned int UnpSize; + unsigned int UnpSizeHigh; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int DictSize; + unsigned int HashType; + char Hash[32]; + unsigned int RedirType; + wchar_t *RedirName; + unsigned int RedirNameSize; + unsigned int DirTarget; + unsigned int MtimeLow; + unsigned int MtimeHigh; + unsigned int CtimeLow; + unsigned int CtimeHigh; + unsigned int AtimeLow; + unsigned int AtimeHigh; + unsigned int Reserved[988]; +}; + + +struct RAROpenArchiveData +{ + char *ArcName; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +#define ROADF_VOLUME 0x0001 +#define ROADF_COMMENT 0x0002 +#define ROADF_LOCK 0x0004 +#define ROADF_SOLID 0x0008 +#define ROADF_NEWNUMBERING 0x0010 +#define ROADF_SIGNED 0x0020 +#define ROADF_RECOVERY 0x0040 +#define ROADF_ENCHEADERS 0x0080 +#define ROADF_FIRSTVOLUME 0x0100 + +#define ROADOF_KEEPBROKEN 0x0001 + +struct RAROpenArchiveDataEx +{ + char *ArcName; + wchar_t *ArcNameW; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + UNRARCALLBACK Callback; + LPARAM UserData; + unsigned int OpFlags; + wchar_t *CmtBufW; + unsigned int Reserved[25]; +}; + +enum UNRARCALLBACK_MESSAGES { + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, + UCM_NEEDPASSWORDW +}; + +typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); +typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); + +#ifdef __cplusplus +extern "C" { +#endif + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); +int PASCAL RARCloseArchive(HANDLE hArcData); +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); +void PASCAL RARSetPassword(HANDLE hArcData,char *Password); +int PASCAL RARGetDllVersion(); + +#ifdef __cplusplus +} +#endif + +#pragma pack(pop) + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/dll_nocrypt.def b/Src/external_dependencies/openmpt-trunk/include/unrar/dll_nocrypt.def new file mode 100644 index 00000000..f7037ea4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/dll_nocrypt.def @@ -0,0 +1,13 @@ +EXPORTS + RAROpenArchive + RAROpenArchiveEx + RARCloseArchive + RARReadHeader + RARReadHeaderEx + RARProcessFile + RARProcessFileW + RARSetCallback + RARSetChangeVolProc + RARSetProcessDataProc +; RARSetPassword + RARGetDllVersion diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/encname.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/encname.cpp new file mode 100644 index 00000000..e1ba1ed7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/encname.cpp @@ -0,0 +1,67 @@ +#include "rar.hpp" + +EncodeFileName::EncodeFileName() +{ + Flags=0; + FlagBits=0; + FlagsPos=0; + DestSize=0; +} + + + + +void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize, + wchar *NameW,size_t MaxDecSize) +{ + size_t EncPos=0,DecPos=0; + byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0; + while (EncPos<EncSize && DecPos<MaxDecSize) + { + if (FlagBits==0) + { + Flags=EncName[EncPos++]; + FlagBits=8; + } + switch(Flags>>6) + { + case 0: + if (EncPos>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos++]; + break; + case 1: + if (EncPos>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); + break; + case 2: + if (EncPos+1>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); + EncPos+=2; + break; + case 3: + { + if (EncPos>=EncSize) + break; + int Length=EncName[EncPos++]; + if ((Length & 0x80)!=0) + { + if (EncPos>=EncSize) + break; + byte Correction=EncName[EncPos++]; + for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++) + NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8); + } + else + for (Length+=2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++) + NameW[DecPos]=Name[DecPos]; + } + break; + } + Flags<<=2; + FlagBits-=2; + } + NameW[DecPos<MaxDecSize ? DecPos:MaxDecSize-1]=0; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/encname.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/encname.hpp new file mode 100644 index 00000000..c6b4ed44 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/encname.hpp @@ -0,0 +1,19 @@ +#ifndef _RAR_ENCNAME_ +#define _RAR_ENCNAME_ + +class EncodeFileName +{ + private: + void AddFlags(byte Value,byte *EncName); + + byte Flags; + uint FlagBits; + size_t FlagsPos; + size_t DestSize; + public: + EncodeFileName(); + size_t Encode(char *Name,wchar *NameW,byte *EncName); + void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.cpp new file mode 100644 index 00000000..d473b1f8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.cpp @@ -0,0 +1,420 @@ +#include "rar.hpp" + +ErrorHandler::ErrorHandler() +{ + Clean(); +} + + +void ErrorHandler::Clean() +{ + ExitCode=RARX_SUCCESS; + ErrCount=0; + EnableBreak=true; + Silent=false; + UserBreak=false; + MainExit=false; + DisableShutdown=false; + ReadErrIgnoreAll=false; +} + + +void ErrorHandler::MemoryError() +{ + MemoryErrorMsg(); + Exit(RARX_MEMORY); +} + + +void ErrorHandler::OpenError(const wchar *FileName) +{ +#ifndef SILENT + OpenErrorMsg(FileName); + Exit(RARX_OPEN); +#endif +} + + +void ErrorHandler::CloseError(const wchar *FileName) +{ + if (!UserBreak) + { + uiMsg(UIERROR_FILECLOSE,FileName); + SysErrMsg(); + } + // We must not call Exit and throw an exception here, because this function + // is called from File object destructor and can be invoked when stack + // unwinding while handling another exception. Throwing a new exception + // when stack unwinding is prohibited and terminates a program. + // If necessary, we can check std::uncaught_exception() before throw. + SetErrorCode(RARX_FATAL); +} + + +void ErrorHandler::ReadError(const wchar *FileName) +{ +#ifndef SILENT + ReadErrorMsg(FileName); +#endif +#if !defined(SILENT) || defined(RARDLL) + Exit(RARX_READ); +#endif +} + + +void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit) +{ + SetErrorCode(RARX_READ); +#if !defined(SILENT) && !defined(SFX_MODULE) + if (!Silent) + { + uiMsg(UIERROR_FILEREAD,UINULL,FileName); + SysErrMsg(); + if (ReadErrIgnoreAll) + Ignore=true; + else + { + bool All=false; + uiAskRepeatRead(FileName,Ignore,All,Retry,Quit); + if (All) + ReadErrIgnoreAll=Ignore=true; + if (Quit) // Disable shutdown if user select Quit in read error prompt. + DisableShutdown=true; + } + return; + } +#endif + Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice. +} + + +void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName) +{ +#ifndef SILENT + WriteErrorMsg(ArcName,FileName); +#endif +#if !defined(SILENT) || defined(RARDLL) + Exit(RARX_WRITE); +#endif +} + + +#ifdef _WIN_ALL +void ErrorHandler::WriteErrorFAT(const wchar *FileName) +{ + SysErrMsg(); + uiMsg(UIERROR_NTFSREQUIRED,FileName); +#if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL) + Exit(RARX_WRITE); +#endif +} +#endif + + +bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull) +{ +#ifndef SILENT + if (!Silent) + { + // We do not display "repeat write" prompt in Android, so we do not + // need the matching system error message. + SysErrMsg(); + bool Repeat=uiAskRepeatWrite(FileName,DiskFull); + if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog. + DisableShutdown=true; + return Repeat; + } +#endif + return false; +} + + +void ErrorHandler::SeekError(const wchar *FileName) +{ + if (!UserBreak) + { + uiMsg(UIERROR_FILESEEK,FileName); + SysErrMsg(); + } +#if !defined(SILENT) || defined(RARDLL) + Exit(RARX_FATAL); +#endif +} + + +void ErrorHandler::GeneralErrMsg(const wchar *fmt,...) +{ + va_list arglist; + va_start(arglist,fmt); + wchar Msg[1024]; + vswprintf(Msg,ASIZE(Msg),fmt,arglist); + uiMsg(UIERROR_GENERALERRMSG,Msg); + SysErrMsg(); + va_end(arglist); +} + + +void ErrorHandler::MemoryErrorMsg() +{ + uiMsg(UIERROR_MEMORY); + SetErrorCode(RARX_MEMORY); +} + + +void ErrorHandler::OpenErrorMsg(const wchar *FileName) +{ + OpenErrorMsg(NULL,FileName); +} + + +void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) +{ + Wait(); // Keep GUI responsive if many files cannot be opened when archiving. + uiMsg(UIERROR_FILEOPEN,ArcName,FileName); + SysErrMsg(); + SetErrorCode(RARX_OPEN); +} + + +void ErrorHandler::CreateErrorMsg(const wchar *FileName) +{ + CreateErrorMsg(NULL,FileName); +} + + +void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_FILECREATE,ArcName,FileName); + SysErrMsg(); + SetErrorCode(RARX_CREATE); +} + + +void ErrorHandler::ReadErrorMsg(const wchar *FileName) +{ + ReadErrorMsg(NULL,FileName); +} + + +void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_FILEREAD,ArcName,FileName); + SysErrMsg(); + SetErrorCode(RARX_READ); +} + + +void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_FILEWRITE,ArcName,FileName); + SysErrMsg(); + SetErrorCode(RARX_WRITE); +} + + +void ErrorHandler::ArcBrokenMsg(const wchar *ArcName) +{ + uiMsg(UIERROR_ARCBROKEN,ArcName); + SetErrorCode(RARX_CRC); +} + + +void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_CHECKSUM,ArcName,FileName); + SetErrorCode(RARX_CRC); +} + + +void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName); + ErrHandler.SetErrorCode(RARX_FATAL); +} + + +void ErrorHandler::Exit(RAR_EXIT ExitCode) +{ + uiAlarm(UIALARM_ERROR); + Throw(ExitCode); +} + + +void ErrorHandler::SetErrorCode(RAR_EXIT Code) +{ + switch(Code) + { + case RARX_WARNING: + case RARX_USERBREAK: + if (ExitCode==RARX_SUCCESS) + ExitCode=Code; + break; + case RARX_CRC: + if (ExitCode!=RARX_BADPWD) + ExitCode=Code; + break; + case RARX_FATAL: + if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING) + ExitCode=RARX_FATAL; + break; + default: + ExitCode=Code; + break; + } + ErrCount++; +} + + +#ifdef _WIN_ALL +BOOL __stdcall ProcessSignal(DWORD SigType) +#else +#if defined(__sun) +extern "C" +#endif +void _stdfunction ProcessSignal(int SigType) +#endif +{ +#ifdef _WIN_ALL + // When a console application is run as a service, this allows the service + // to continue running after the user logs off. + if (SigType==CTRL_LOGOFF_EVENT) + return TRUE; +#endif + + ErrHandler.UserBreak=true; + ErrHandler.SetDisableShutdown(); + mprintf(St(MBreak)); + +#ifdef _WIN_ALL + // Let the main thread to handle 'throw' and destroy file objects. + for (uint I=0;!ErrHandler.MainExit && I<50;I++) + Sleep(100); +#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL) + ExtRes.UnloadDLL(); +#endif + exit(RARX_USERBREAK); +#endif + +#ifdef _UNIX + static uint BreakCount=0; + // User continues to press Ctrl+C, exit immediately without cleanup. + if (++BreakCount>1) + exit(RARX_USERBREAK); + // Otherwise return from signal handler and let Wait() function to close + // files and quit. We cannot use the same approach as in Windows, + // because Unix signal handler can block execution of our main code. +#endif + +#if defined(_WIN_ALL) && !defined(_MSC_VER) + // Never reached, just to avoid a compiler warning + return TRUE; +#endif +} + + +void ErrorHandler::SetSignalHandlers(bool Enable) +{ + EnableBreak=Enable; +#ifdef _WIN_ALL + SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); +#else + signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); + signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); +#endif +} + + +void ErrorHandler::Throw(RAR_EXIT Code) +{ + if (Code==RARX_USERBREAK && !EnableBreak) + return; +#if !defined(SILENT) + // Do not write "aborted" when just displaying online help. + if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) + mprintf(L"\n%s\n",St(MProgAborted)); +#endif + SetErrorCode(Code); + throw Code; +} + + +bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) +{ +#ifndef SILENT +#ifdef _WIN_ALL + int ErrType=GetLastError(); + if (ErrType!=0) + return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), + Msg,(DWORD)Size,NULL)!=0; +#endif + +#if defined(_UNIX) || defined(_EMX) + if (errno!=0) + { + char *err=strerror(errno); + if (err!=NULL) + { + CharToWide(err,Msg,Size); + return true; + } + } +#endif +#endif + return false; +} + + +void ErrorHandler::SysErrMsg() +{ +#ifndef SILENT + wchar Msg[1024]; + if (!GetSysErrMsg(Msg,ASIZE(Msg))) + return; +#ifdef _WIN_ALL + wchar *CurMsg=Msg; + while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines. + { + while (*CurMsg=='\r' || *CurMsg=='\n') + CurMsg++; + if (*CurMsg==0) + break; + wchar *EndMsg=wcschr(CurMsg,'\r'); + if (EndMsg==NULL) + EndMsg=wcschr(CurMsg,'\n'); + if (EndMsg!=NULL) + { + *EndMsg=0; + EndMsg++; + } + uiMsg(UIERROR_SYSERRMSG,CurMsg); + CurMsg=EndMsg; + } +#endif + +#if defined(_UNIX) || defined(_EMX) + uiMsg(UIERROR_SYSERRMSG,Msg); +#endif + +#endif +} + + +int ErrorHandler::GetSystemErrorCode() +{ +#ifdef _WIN_ALL + return GetLastError(); +#else + return errno; +#endif +} + + +void ErrorHandler::SetSystemErrorCode(int Code) +{ +#ifdef _WIN_ALL + SetLastError(Code); +#else + errno=Code; +#endif +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.hpp new file mode 100644 index 00000000..06f4f616 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/errhnd.hpp @@ -0,0 +1,75 @@ +#ifndef _RAR_ERRHANDLER_ +#define _RAR_ERRHANDLER_ + +enum RAR_EXIT // RAR exit code. +{ + RARX_SUCCESS = 0, + RARX_WARNING = 1, + RARX_FATAL = 2, + RARX_CRC = 3, + RARX_LOCK = 4, + RARX_WRITE = 5, + RARX_OPEN = 6, + RARX_USERERROR = 7, + RARX_MEMORY = 8, + RARX_CREATE = 9, + RARX_NOFILES = 10, + RARX_BADPWD = 11, + RARX_READ = 12, + RARX_USERBREAK = 255 +}; + + +class ErrorHandler +{ + private: + RAR_EXIT ExitCode; + uint ErrCount; + bool EnableBreak; + bool Silent; + bool DisableShutdown; // Shutdown is not suitable after last error. + bool ReadErrIgnoreAll; + public: + ErrorHandler(); + void Clean(); + void MemoryError(); + void OpenError(const wchar *FileName); + void CloseError(const wchar *FileName); + void ReadError(const wchar *FileName); + void AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit); + void WriteError(const wchar *ArcName,const wchar *FileName); + void WriteErrorFAT(const wchar *FileName); + bool AskRepeatWrite(const wchar *FileName,bool DiskFull); + void SeekError(const wchar *FileName); + void GeneralErrMsg(const wchar *fmt,...); + void MemoryErrorMsg(); + void OpenErrorMsg(const wchar *FileName); + void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); + void CreateErrorMsg(const wchar *FileName); + void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); + void ReadErrorMsg(const wchar *FileName); + void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); + void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); + void ArcBrokenMsg(const wchar *ArcName); + void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); + void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); + void Exit(RAR_EXIT ExitCode); + void SetErrorCode(RAR_EXIT Code); + RAR_EXIT GetErrorCode() {return ExitCode;} + uint GetErrorCount() {return ErrCount;} + void SetSignalHandlers(bool Enable); + void Throw(RAR_EXIT Code); + void SetSilent(bool Mode) {Silent=Mode;} + bool GetSysErrMsg(wchar *Msg,size_t Size); + void SysErrMsg(); + int GetSystemErrorCode(); + void SetSystemErrorCode(int Code); + void SetDisableShutdown() {DisableShutdown=true;} + bool IsShutdownEnabled() {return !DisableShutdown;} + + bool UserBreak; // Ctrl+Break is pressed. + bool MainExit; // main() is completed. +}; + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.cpp new file mode 100644 index 00000000..5cb90a40 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.cpp @@ -0,0 +1,178 @@ +#include "rar.hpp" + +#include "hardlinks.cpp" +#include "win32stm.cpp" + +#ifdef _WIN_ALL +#include "win32acl.cpp" +#include "win32lnk.cpp" +#endif + +#ifdef _UNIX +#include "uowners.cpp" +#ifdef SAVE_LINKS +#include "ulinks.cpp" +#endif +#endif + + + +// RAR2 service header extra records. +#ifndef SFX_MODULE +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) +{ + if (Cmd->Test) + return; + switch(Arc.SubBlockHead.SubType) + { +#ifdef _UNIX + case UO_HEAD: + if (Cmd->ProcessOwners) + ExtractUnixOwner20(Arc,Name); + break; +#endif +#ifdef _WIN_ALL + case NTACL_HEAD: + if (Cmd->ProcessOwners) + ExtractACL20(Arc,Name); + break; + case STREAM_HEAD: + ExtractStreams20(Arc,Name); + break; +#endif + } +} +#endif + + +// RAR3 and RAR5 service header extra records. +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) +{ +#ifdef _UNIX + if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && + Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwner30(Arc,Name); +#endif +#ifdef _WIN_ALL + if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) + ExtractACL(Arc,Name); + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + ExtractStreams(Arc,Name,Cmd->Test); +#endif +} + + +// Extra data stored directly in file header. +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) +{ +#ifdef _UNIX + if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) + SetUnixOwner(Arc,Name); +#endif +} + + + + +// Calculate a number of path components except \. and \.. +static int CalcAllowedDepth(const wchar *Name) +{ + int AllowedDepth=0; + while (*Name!=0) + { + if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) + { + bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); + bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); + if (!Dot && !Dot2) + AllowedDepth++; + } + Name++; + } + return AllowedDepth; +} + + +// Check if all existing path components are directories and not links. +static bool LinkInPath(const wchar *Name) +{ + wchar Path[NM]; + if (wcslen(Name)>=ASIZE(Path)) + return true; // It should not be that long, skip. + wcsncpyz(Path,Name,ASIZE(Path)); + for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) + return true; + } + return false; +} + + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) +{ + // Catch root dir based /path/file paths also as stuff like \\?\. + // Do not check PrepSrcName here, it can be root based if destination path + // is a root based. + if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) + return false; + + // Number of ".." in link target. + int UpLevels=0; + for (int Pos=0;*TargetName!=0;Pos++) + { + bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && + (IsPathDiv(TargetName[2]) || TargetName[2]==0) && + (Pos==0 || IsPathDiv(*(TargetName-1))); + if (Dot2) + UpLevels++; + TargetName++; + } + // If link target includes "..", it must not have another links + // in the path, because they can bypass our safety check. For example, + // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next + // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. + if (UpLevels>0 && LinkInPath(PrepSrcName)) + return false; + + // We could check just prepared src name, but for extra safety + // we check both original (as from archive header) and prepared + // (after applying the destination path and -ep switches) names. + + int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth. + + // Remove the destination path from prepared name if any. We should not + // count the destination path depth, because the link target must point + // inside of this path, not outside of it. + size_t ExtrPathLength=wcslen(Cmd->ExtrPath); + if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) + { + PrepSrcName+=ExtrPathLength; + while (IsPathDiv(*PrepSrcName)) + PrepSrcName++; + } + int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); + + return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; +} + + +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +{ +#if defined(SAVE_LINKS) && defined(_UNIX) + // For RAR 3.x archives we process links even in test mode to skip link data. + if (Arc.Format==RARFMT15) + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); + if (Arc.Format==RARFMT50) + return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); +#elif defined _WIN_ALL + // RAR 5.0 archives store link information in file header, so there is + // no need to additionally test it if we do not create a file. + if (Arc.Format==RARFMT50) + return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); +#endif + return false; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.hpp new file mode 100644 index 00000000..f3c7511b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/extinfo.hpp @@ -0,0 +1,23 @@ +#ifndef _RAR_EXTINFO_ +#define _RAR_EXTINFO_ + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); +#ifdef _UNIX +void SetUnixOwner(Archive &Arc,const wchar *FileName); +#endif + +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); + +#ifdef _WIN_ALL +bool SetPrivilege(LPCTSTR PrivName); +#endif + +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/extract.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/extract.cpp new file mode 100644 index 00000000..cdba6377 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/extract.cpp @@ -0,0 +1,1369 @@ +#include "rar.hpp" + +CmdExtract::CmdExtract(CommandData *Cmd) +{ + CmdExtract::Cmd=Cmd; + + *ArcName=0; + + *DestFileName=0; + + TotalFileCount=0; + Unp=new Unpack(&DataIO); +#ifdef RAR_SMP + Unp->SetThreads(Cmd->Threads); +#endif +} + + +CmdExtract::~CmdExtract() +{ + delete Unp; +} + + +void CmdExtract::DoExtract() +{ +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + Fat32=NotFat32=false; +#endif + PasswordCancelled=false; + DataIO.SetCurrentCommand(Cmd->Command[0]); + + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) + DataIO.TotalArcSize+=FD.Size; + + Cmd->ArcNames.Rewind(); + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + + ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit(). + UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit(). + while (true) + { + EXTRACT_ARC_CODE Code=ExtractArchive(); + if (Code!=EXTRACT_ARC_REPEAT) + break; + } + DataIO.ProcessedArcSize+=DataIO.LastArcSize; + } + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (TotalFileCount==0 && Cmd->Command[0]!='I' && + ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. + { + if (!PasswordCancelled) + uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); + + // Other error codes may explain a reason of "no files extracted" clearer, + // so set it only if no other errors found (wrong mask set by user). + if (ErrHandler.GetErrorCode()==RARX_SUCCESS) + ErrHandler.SetErrorCode(RARX_NOFILES); + } + else + if (!Cmd->DisableDone) + if (Cmd->Command[0]=='I') + mprintf(St(MDone)); + else + if (ErrHandler.GetErrorCount()==0) + mprintf(St(MExtrAllOk)); + else + mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); +} + + +void CmdExtract::ExtractArchiveInit(Archive &Arc) +{ + DataIO.AdjustTotalArcSize(&Arc); + + FileCount=0; + MatchedArgs=0; +#ifndef SFX_MODULE + FirstFile=true; +#endif + + GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); + + DataIO.UnpVolume=false; + + PrevProcessed=false; + AllMatchesExact=true; + AnySolidDataUnpackedWell=false; + + StartTime.SetCurrentTime(); +} + + +EXTRACT_ARC_CODE CmdExtract::ExtractArchive() +{ + Archive Arc(Cmd); + if (*Cmd->UseStdin!=0) + { + Arc.SetHandleType(FILE_HANDLESTD); +#ifdef USE_QOPEN + Arc.SetProhibitQOpen(true); +#endif + } + else + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache. + if (*Cmd->Command=='T' || Cmd->Test) + ResetFileCache(ArcName); // Reset the file cache when testing an archive. +#endif + if (!Arc.WOpen(ArcName)) + return EXTRACT_ARC_NEXT; + } + + if (!Arc.IsArchive(true)) + { +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (CmpExt(ArcName,L"rev")) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + RecVolumesTest(Cmd,NULL,ArcName); + TotalFileCount++; // Suppress "No files to extract" message. + return EXTRACT_ARC_NEXT; + } +#endif + + mprintf(St(MNotRAR),ArcName); + +#ifndef SFX_MODULE + if (CmpExt(ArcName,L"rar")) +#endif + ErrHandler.SetErrorCode(RARX_WARNING); + return EXTRACT_ARC_NEXT; + } + + if (Arc.FailedHeaderDecryption) // Bad archive password. + return EXTRACT_ARC_NEXT; + +#ifndef SFX_MODULE + if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + } +#endif + + int64 VolumeSetSize=0; // Total size of volumes after the current volume. + + if (Arc.Volume) + { +#ifndef SFX_MODULE + // Try to speed up extraction for independent solid volumes by starting + // extraction from non-first volume if we can. + if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering)) + { + UseExactVolName=true; + return EXTRACT_ARC_REPEAT; + } +#endif + + // Calculate the total size of all accessible volumes. + // This size is necessary to display the correct total progress indicator. + + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); + + while (true) + { + // First volume is already added to DataIO.TotalArcSize + // in initial TotalArcSize calculation in DoExtract. + // So we skip it and start from second volume. + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FindData FD; + if (FindFile::FastFind(NextName,&FD)) + VolumeSetSize+=FD.Size; + else + break; + } + DataIO.TotalArcSize+=VolumeSetSize; + } + + ExtractArchiveInit(Arc); + + if (*Cmd->Command=='T' || *Cmd->Command=='I') + Cmd->Test=true; + + + if (*Cmd->Command=='I') + { + Cmd->DisablePercentage=true; + } + else + uiStartArchiveExtract(!Cmd->Test,ArcName); + + Arc.ViewComment(); + + + while (1) + { + size_t Size=Arc.ReadHeader(); + + + bool Repeat=false; + if (!ExtractCurrentFile(Arc,Size,Repeat)) + if (Repeat) + { + // If we started extraction from not first volume and need to + // restart it from first, we must set DataIO.TotalArcSize to size + // of new first volume to display the total progress correctly. + FindData NewArc; + if (FindFile::FastFind(ArcName,&NewArc)) + DataIO.TotalArcSize=NewArc.Size; + return EXTRACT_ARC_REPEAT; + } + else + break; + } + + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Cmd->Test && Arc.Volume) + RecVolumesTest(Cmd,&Arc,ArcName); +#endif + + return EXTRACT_ARC_NEXT; +} + + +bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) +{ + wchar Command=Cmd->Command[0]; + if (HeaderSize==0) + if (DataIO.UnpVolume) + { +#ifdef NOVOLUME + return false; +#else + // Supposing we unpack an old RAR volume without the end of archive + // record and last file is not split between volumes. + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return false; + } +#endif + } + else + return false; + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType!=HEAD_FILE) + { +#ifndef SFX_MODULE + if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) + SetExtraInfo20(Cmd,Arc,DestFileName); +#endif + if (HeaderType==HEAD_SERVICE && PrevProcessed) + SetExtraInfo(Cmd,Arc,DestFileName); + if (HeaderType==HEAD_ENDARC) + if (Arc.EndArcHead.NextVolume) + { +#ifdef NOVOLUME + return false; +#else + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return false; + } + Arc.Seek(Arc.CurBlockPos,SEEK_SET); + return true; +#endif + } + else + return false; + Arc.SeekToNext(); + return true; + } + PrevProcessed=false; + + // We can get negative sizes in corrupt archive and it is unacceptable + // for size comparisons in ComprDataIO::UnpRead, where we cast sizes + // to size_t and can exceed another read or available size. We could fix it + // when reading an archive. But we prefer to do it here, because this + // function is called directly in unrar.dll, so we fix bad parameters + // passed to dll. Also we want to see real negative sizes in the listing + // of corrupt archive. To prevent uninitialized data access perform + // these checks after rejecting zero length and non-file headers above. + if (Arc.FileHead.PackSize<0) + Arc.FileHead.PackSize=0; + if (Arc.FileHead.UnpSize<0) + Arc.FileHead.UnpSize=0; + + if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) + return false; + + int MatchType=MATCH_WILDSUBPATH; + + bool EqualNames=false; + wchar MatchedArg[NM]; + int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg)); + bool MatchFound=MatchNumber!=0; +#ifndef SFX_MODULE + if (Cmd->ExclPath==EXCL_BASEPATH) + { + wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); + *PointToName(Cmd->ArcPath)=0; + if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. + *Cmd->ArcPath=0; + } +#endif + if (MatchFound && !EqualNames) + AllMatchesExact=false; + + Arc.ConvertAttributes(); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName) + { + wchar CurVolName[NM]; + wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); + GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName)); + + if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) + { + wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction". + // If first volume name does not match the current name and if such + // volume name really exists, let's unpack from this first volume. + Repeat=true; + return false; + } +#ifndef RARDLL + if (!ReconstructDone) + { + ReconstructDone=true; + if (RecVolumesRestore(Cmd,Arc.FileName,true)) + { + Repeat=true; + return false; + } + } +#endif + wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); + } +#endif + + wchar ArcFileName[NM]; + ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName)); + + if (Arc.FileHead.Version) + { + if (Cmd->VersionControl!=1 && !EqualNames) + { + if (Cmd->VersionControl==0) + MatchFound=false; + int Version=ParseVersionFileName(ArcFileName,false); + if (Cmd->VersionControl-1==Version) + ParseVersionFileName(ArcFileName,true); + else + MatchFound=false; + } + } + else + if (!Arc.IsArcDir() && Cmd->VersionControl>1) + MatchFound=false; + + DataIO.UnpVolume=Arc.FileHead.SplitAfter; + DataIO.NextVolumeMissing=false; + + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); + + bool ExtrFile=false; + bool SkipSolid=false; + +#ifndef SFX_MODULE + if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore) + { + if (MatchFound) + { + uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName); +#ifdef RARDLL + Cmd->DllError=ERAR_BAD_DATA; +#endif + ErrHandler.SetErrorCode(RARX_OPEN); + } + MatchFound=false; + } + + FirstFile=false; +#endif + + if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) + if (Arc.Solid) + return false; // Abort the entire extraction for solid archive. + else + MatchFound=false; // Skip only the current file for non-solid archive. + + if (MatchFound || (SkipSolid=Arc.Solid)!=0) + { + // First common call of uiStartFileExtract. It is done before overwrite + // prompts, so if SkipSolid state is changed below, we'll need to make + // additional uiStartFileExtract calls with updated parameters. + if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) + return false; + + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); + + // DestFileName can be set empty in case of excessive -ap switch. + ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; + + /* // OPENMPT ADDITION + if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) + { + FindData FD; + if (FindFile::FastFind(DestFileName,&FD)) + { + if (FD.mtime >= Arc.FileHead.mtime) + { + // If directory already exists and its modification time is newer + // than start of extraction, it is likely it was created + // when creating a path to one of already extracted items. + // In such case we'll better update its time even if archived + // directory is older. + + if (!FD.IsDir || FD.mtime<StartTime) + ExtrFile=false; + } + } + else + if (Cmd->FreshFiles) + ExtrFile=false; + } + */ // OPENMPT ADDITION + + if (!CheckUnpVer(Arc,ArcFileName)) + { + ErrHandler.SetErrorCode(RARX_FATAL); +#ifdef RARDLL + Cmd->DllError=ERAR_UNKNOWN_FORMAT; +#endif + Arc.SeekToNext(); + return !Arc.Solid; // Can try extracting next file only in non-solid archive. + } + + while (true) // Repeat the password prompt for wrong and empty passwords. + { + if (Arc.FileHead.Encrypted) + { + // Stop archive extracting if user cancelled a password prompt. +#ifdef RARDLL + if (!ExtrDllGetPassword()) + { + Cmd->DllError=ERAR_MISSING_PASSWORD; + return false; + } +#else + if (!ExtrGetPassword(Arc,ArcFileName)) + { + PasswordCancelled=true; + return false; + } +#endif + } + + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); +#endif + + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); + + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && + !Arc.BrokenHeader) + { + if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); + Cmd->Password.Clean(); + + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. +#ifndef RARDLL + continue; // Request a password again. +#endif + } +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_PASSWORD. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_PASSWORD; +#endif + ErrHandler.SetErrorCode(RARX_BADPWD); + ExtrFile=false; + } + break; + } + +#ifdef RARDLL + if (*Cmd->DllDestName!=0) + wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); +#endif + + File CurFile; + + bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; + if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) + { + if (ExtrFile && Command!='P' && !Cmd->Test) + { + // Overwrite prompt for symbolic and hard links. + bool UserReject=false; + if (FileExist(DestFileName) && !UserReject) + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + if (UserReject) + ExtrFile=false; + } + } + else + if (Arc.IsArcDir()) + { + if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + return true; + TotalFileCount++; + ExtrCreateDir(Arc,ArcFileName); + // It is important to not increment MatchedArgs here, so we extract + // dir with its entire contents and not dir record only even if + // dir record precedes files. + return true; + } + else + if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). + ExtrFile=ExtrCreateFile(Arc,CurFile); + + if (!ExtrFile && Arc.Solid) + { + SkipSolid=true; + ExtrFile=true; + + // We changed SkipSolid, so we need to call uiStartFileExtract + // with "Skip" parameter to change the operation status + // from "extracting" to "skipping". For example, it can be necessary + // if user answered "No" to overwrite prompt when unpacking + // a solid archive. + if (!uiStartFileExtract(ArcFileName,false,false,true)) + return false; + } + if (ExtrFile) + { + // Set it in test mode, so we also test subheaders such as NTFS streams + // after tested file. + if (Cmd->Test) + PrevProcessed=true; + + bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk. + + if (!SkipSolid) + { + if (!TestMode && Command!='P' && CurFile.IsDevice()) + { + uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName); + ErrHandler.WriteError(Arc.FileName,DestFileName); + } + TotalFileCount++; + } + FileCount++; + if (Command!='I' && !Cmd->DisableNames) + if (SkipSolid) + mprintf(St(MExtrSkipFile),ArcFileName); + else + switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. + { + case 'T': + mprintf(St(MExtrTestFile),ArcFileName); + break; +#ifndef SFX_MODULE + case 'P': + mprintf(St(MExtrPrinting),ArcFileName); + break; +#endif + case 'X': + case 'E': + mprintf(St(MExtrFile),DestFileName); + break; + } + if (!Cmd->DisablePercentage && !Cmd->DisableNames) + mprintf(L" "); + if (Cmd->DisableNames) + uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode. + + DataIO.CurUnpRead=0; + DataIO.CurUnpWrite=0; + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize); + DataIO.SetFiles(&Arc,&CurFile); + DataIO.SetTestMode(TestMode); + DataIO.SetSkipUnpCRC(SkipSolid); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + if (!TestMode && !Arc.BrokenHeader && + Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32)) + { + if (!Fat32) // Not detected yet. + NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath)); + if (Fat32) + uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit. + } +#endif + + uint64 Preallocated=0; + if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && + Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() && + (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) + { + CurFile.Prealloc(Arc.FileHead.UnpSize); + Preallocated=Arc.FileHead.UnpSize; + } + CurFile.SetAllowDelete(!Cmd->KeepBroken); + + bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; + bool ShowChecksum=true; // Display checksum verification result. + + bool LinkSuccess=true; // Assume success for test mode. + if (LinkEntry) + { + /* // OPENMPT ADDITION + FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType; + + if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) + { + wchar RedirName[NM]; + ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + + wchar NameExisting[NM]; + ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); + if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. + if (Type==FSREDIR_HARDLINK) + LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); + else + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); + } + else + if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) + { + if (FileCreateMode) + LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); + } + else + { + uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName); + LinkSuccess=false; + } + + if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode) + { + // RAR 5.x links have a valid data checksum even in case of + // failure, because they do not store any data. + // We do not want to display "OK" in this case. + // For 4.x symlinks we verify the checksum only when extracting, + // but not when testing an archive. + ShowChecksum=false; + } + PrevProcessed=FileCreateMode && LinkSuccess; + */ // OPENMPT ADDITION + } + else + if (!Arc.FileHead.SplitBefore) + if (Arc.FileHead.Method==0) + UnstoreFile(DataIO,Arc.FileHead.UnpSize); + else + { + Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + Unp->SetDestSize(Arc.FileHead.UnpSize); +#ifndef SFX_MODULE + if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) + Unp->DoUnpack(15,FileCount>1 && Arc.Solid); + else +#endif + Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid); + } + + Arc.SeekToNext(); + + // We check for "split after" flag to detect partially extracted files + // from incomplete volume sets. For them file header contains packed + // data hash, which must not be compared against unpacked data hash + // to prevent accidental match. Moreover, for -m0 volumes packed data + // hash would match truncated unpacked data hash and lead to fake "OK" + // in incomplete volume set. + bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL); + + // We set AnySolidDataUnpackedWell to true if we found at least one + // valid non-zero solid file in preceding solid stream. If it is true + // and if current encrypted file is broken, we do not need to hint + // about a wrong password and can report CRC error only. + if (!Arc.FileHead.Solid) + AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. + else + if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC) + AnySolidDataUnpackedWell=true; + + bool BrokenFile=false; + + // Checksum is not calculated in skip solid mode for performance reason. + if (!SkipSolid && ShowChecksum) + { + if (ValidCRC) + { + if (Command!='P' && Command!='I' && !Cmd->DisableNames) + mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", + Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); + } + else + { + if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || + Arc.BrokenHeader) && !AnySolidDataUnpackedWell) + uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName); + else + uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName); + BrokenFile=true; + ErrHandler.SetErrorCode(RARX_CRC); +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume + // or ERAR_BAD_PASSWORD for RAR5 wrong password, + // we should not replace it with less precise ERAR_BAD_DATA. + if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD) + Cmd->DllError=ERAR_BAD_DATA; +#endif + } + } + else + { + // We check SkipSolid to remove percent for skipped solid files only. + // We must not apply these \b to links with ShowChecksum==false + // and their possible error messages. + if (SkipSolid) + mprintf(L"\b\b\b\b\b "); + } + + /* // OPENMPT ADDITION + // If we successfully unpacked a hard link, we wish to set its file + // attributes. Hard link shares file metadata with link target, + // so we do not need to set link time or owner. But when we overwrite + // an existing link, we can call PrepareToDelete(), which affects + // link target attributes as well. So we set link attributes to restore + // both target and link attributes if PrepareToDelete() changed them. + bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess; + + if (!TestMode && (Command=='X' || Command=='E') && + (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && + (!BrokenFile || Cmd->KeepBroken)) + { + // Below we use DestFileName instead of CurFile.FileName, + // so we can set file attributes also for hard links, which do not + // have the open CurFile. These strings are the same for other items. + + if (!SetAttrOnly) + { + // We could preallocate more space that really written to broken file + // or file with crafted header. + if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) + CurFile.Truncate(); + + + CurFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + CurFile.Close(); + + SetFileHeaderExtra(Cmd,Arc,DestFileName); + + CurFile.SetCloseFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + } + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) + SetFileCompression(DestFileName,true); + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; +#endif + if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr)) + { + uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName); + // Android cannot set file attributes and while UIERROR_FILEATTR + // above is handled by Android RAR silently, this call would cause + // "Operation not permitted" message for every unpacked file. + ErrHandler.SysErrMsg(); + } + + PrevProcessed=true; + } + */ // OPENMPT ADDITION + } + } + // It is important to increment it for files, but not dirs. So we extract + // dir with its entire contents, not just dir record only even if dir + // record precedes files. + if (MatchFound) + MatchedArgs++; + if (DataIO.NextVolumeMissing) + return false; + if (!ExtrFile) + if (!Arc.Solid) + Arc.SeekToNext(); + else + if (!SkipSolid) + return false; + return true; +} + + +void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) +{ + Array<byte> Buffer(File::CopyBufferSize()); + while (true) + { + int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (ReadSize<=0) + break; + int WriteSize=ReadSize<DestUnpSize ? ReadSize:(int)DestUnpSize; + if (WriteSize>0) + { + DataIO.UnpWrite(&Buffer[0],WriteSize); + DestUnpSize-=WriteSize; + } + } +} + + +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + File Existing; + if (!Existing.WOpen(NameExisting)) + { + uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); + uiMsg(UIERROR_FILECOPYHINT,ArcName); +#ifdef RARDLL + Cmd->DllError=ERAR_EREFERENCE; +#endif + return false; + } + + Array<char> Buffer(0x100000); + int64 CopySize=0; + + while (true) + { + Wait(); + int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); + if (ReadSize==0) + break; + New.Write(&Buffer[0],ReadSize); + CopySize+=ReadSize; + } + + return true; +} + + +void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) +{ + *DestName = L'*'; // OPENMPT ADDITION + return; // OPENMPT ADDITION + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); + + if (*Cmd->ExtrPath!=0) + { + wchar LastChar=*PointToLastChar(Cmd->ExtrPath); + // We need IsPathDiv check here to correctly handle Unix forward slash + // in the end of destination path in Windows: rar x arc dest/ + // so we call IsPathDiv first instead of just calling AddEndSlash, + // which checks for only one type of path separator. + // IsDriveDiv is needed for current drive dir: rar x arc d: + if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) + { + // Destination path can be without trailing slash if it come from GUI shell. + AddEndSlash(DestName,DestSize); + } + } + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE) + { + switch(Cmd->AppendArcNameToPath) + { + case APPENDARCNAME_DESTPATH: // To subdir of destination path. + wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNDIR: // To archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + RemoveNameFromPath(DestName); + break; + } + AddEndSlash(DestName,DestSize); + } +#endif + +#ifndef SFX_MODULE + wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath; + size_t ArcPathLength=wcslen(ArcPath); + if (ArcPathLength>0) + { + size_t NameLength=wcslen(ArcFileName); + if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 && + (IsPathDiv(ArcPath[ArcPathLength-1]) || + IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) + { + ArcFileName+=Min(ArcPathLength,NameLength); + while (IsPathDiv(*ArcFileName)) + ArcFileName++; + if (*ArcFileName==0) // Excessive -ap switch. + { + *DestName=0; + return; + } + } + } +#endif + + wchar Command=Cmd->Command[0]; + // Use -ep3 only in systems, where disk letters are exist, not in Unix. + bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); + + // We do not use any user specified destination paths when extracting + // absolute paths in -ep3 mode. + if (AbsPaths) + *DestName=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + wcsncatz(DestName,PointToName(ArcFileName),DestSize); + else + wcsncatz(DestName,ArcFileName,DestSize); + +#ifdef _WIN_ALL + // Must do after Cmd->ArcPath processing above, so file name and arc path + // trailing spaces are in sync. + if (!Cmd->AllowIncompatNames) + MakeNameCompatible(DestName,DestSize); +#endif + + wchar DiskLetter=toupperw(DestName[0]); + + if (AbsPaths) + { + if (DestName[1]=='_' && IsPathDiv(DestName[2]) && + DiskLetter>='A' && DiskLetter<='Z') + DestName[1]=':'; + else + if (DestName[0]=='_' && DestName[1]=='_') + { + // Convert __server\share to \\server\share. + DestName[0]=CPATHDIVIDER; + DestName[1]=CPATHDIVIDER; + } + } +} + + +#ifdef RARDLL +bool CmdExtract::ExtrDllGetPassword() +{ + return false; // OPENMPT ADDITION + if (!Cmd->Password.IsSet()) + { + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + Cmd->ManualPassword=true; + } + if (!Cmd->Password.IsSet()) + return false; + } + return true; +} +#endif + + +#ifndef RARDLL +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) +{ + if (!Cmd->Password.IsSet()) + { + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) + { + // Suppress "test is ok" message if user cancelled the password prompt. + uiMsg(UIERROR_INCERRCOUNT); + return false; + } + Cmd->ManualPassword=true; + } +#if !defined(SILENT) + else + if (!GlobalPassword && !Arc.FileHead.Solid) + { + eprintf(St(MUseCurPsw),ArcFileName); + switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) + { + case -1: + ErrHandler.Exit(RARX_USERBREAK); + case 2: + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) + return false; + break; + case 3: + GlobalPassword=true; + break; + } + } +#endif + return true; +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) +{ + if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS) + { + // We need the password in OEM encoding if file was encrypted by + // native RAR/DOS (not extender based). Let's make the conversion. + wchar PlainPsw[MAXPASSWORD]; + Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw)); + char PswA[MAXPASSWORD]; + CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); + PswA[ASIZE(PswA)-1]=0; + CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); + DestPwd.Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(PswA,sizeof(PswA)); + } +} +#endif + + +void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) +{ + return; // OPENMPT ADDITION + if (Cmd->Test) + { + if (!Cmd->DisableNames) + { + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(L" %s",St(MOk)); + } + return; + } + + MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + bool DirExist=false; + if (MDCode!=MKDIR_SUCCESS) + { + DirExist=FileExist(DestFileName); + if (DirExist && !IsDir(GetFileAttr(DestFileName))) + { + // File with name same as this directory exists. Propose user + // to overwrite it. + bool UserReject; + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + DirExist=false; + } + if (!DirExist) + { + CreatePath(DestFileName,true,Cmd->DisableNames); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + MakeNameUsable(DestFileName,true); +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); + if (!DirExist) + { + CreatePath(DestFileName,true,Cmd->DisableNames); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + } + } + } + } + if (MDCode==MKDIR_SUCCESS) + { + if (!Cmd->DisableNames) + { + mprintf(St(MCreatDir),DestFileName); + mprintf(L" %s",St(MOk)); + } + PrevProcessed=true; + } + else + if (DirExist) + { + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(DestFileName,Arc.FileHead.FileAttr); + PrevProcessed=true; + } + else + { + uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName); + ErrHandler.SysErrMsg(); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + ErrHandler.SetErrorCode(RARX_CREATE); + } + if (PrevProcessed) + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE) + SetFileCompression(DestFileName,true); +#endif + SetFileHeaderExtra(Cmd,Arc,DestFileName); + SetDirTime(DestFileName, + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + } +} + + +bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) +{ + return true; // OPENMPT ADDITION + bool Success=true; + wchar Command=Cmd->Command[0]; +#if !defined(SFX_MODULE) + if (Command=='P') + CurFile.SetHandleType(FILE_HANDLESTD); +#endif + if ((Command=='E' || Command=='X') && !Cmd->Test) + { + bool UserReject; + // Specify "write only" mode to avoid OpenIndiana NAS problems + // with SetFileTime and read+write files. + if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { + Success=false; + if (!UserReject) + { + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName))) + uiMsg(UIERROR_DIRNAMEEXISTS); + +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + if (!IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + + MakeNameUsable(DestFileName,true); + + CreatePath(DestFileName,true,Cmd->DisableNames); + if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + Success=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + } + } + } + } + return Success; +} + + +bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) +{ + bool WrongVer; + if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. + WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; + else + { +#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. + WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK; +#else // All formats since 1.3 for RAR. + WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK; +#endif + } + + // We can unpack stored files regardless of compression version field. + if (Arc.FileHead.Method==0) + WrongVer=false; + + if (WrongVer) + { + ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); + uiMsg(UIERROR_NEWERRAR,Arc.FileName); + } + return !WrongVer; +} + + +#ifndef SFX_MODULE +// To speed up solid volumes extraction, try to find a non-first start volume, +// which still allows to unpack all files. It is possible for independent +// solid volumes with solid statistics reset in the beginning. +bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) +{ + wchar *ArgName=Cmd->FileArgs.GetString(); + Cmd->FileArgs.Rewind(); + if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) + return false; // No need to check further for * and *.* masks. + + wchar StartName[NM]; + *StartName=0; + + // Start search from first volume if all volumes preceding current are available. + wchar NextName[NM]; + GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName)); + + bool Matched=false; + while (!Matched) + { + Archive Arc(Cmd); + if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume) + break; + + bool OpenNext=false; + while (Arc.ReadHeader()>0) + { + Wait(); + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { + OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume. + break; + } + if (HeaderType==HEAD_FILE) + { + if (!Arc.FileHead.SplitBefore) + { + if (!Arc.FileHead.Solid) // Can start extraction from here. + wcsncpyz(StartName,NextName,ASIZE(StartName)); + + if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) + { + Matched=true; // First matched file found, must stop further scan. + break; + } + } + if (Arc.FileHead.SplitAfter) + { + OpenNext=true; // Allow open next volume. + break; + } + } + Arc.SeekToNext(); + } + Arc.Close(); + + if (!OpenNext) + break; + + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + } + bool NewStartFound=wcscmp(VolName,StartName)!=0; + if (NewStartFound) // Found a new volume to start extraction. + wcsncpyz(ArcName,StartName,ASIZE(ArcName)); + + return NewStartFound; +} +#endif + + +#ifndef SFX_MODULE +// Return the first volume name if all volumes preceding the specified +// are available. Otherwise return the specified volume name. +void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize) +{ + wchar FirstVolName[NM]; + VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering); + wchar NextName[NM]; + wcsncpyz(NextName,FirstVolName,ASIZE(NextName)); + wchar ResultName[NM]; + wcsncpyz(ResultName,SrcName,ASIZE(ResultName)); + while (true) + { + if (wcscmp(SrcName,NextName)==0) + { + wcsncpyz(ResultName,FirstVolName,DestSize); + break; + } + if (!FileExist(NextName)) + break; + NextVolumeName(NextName,ASIZE(NextName),!NewNumbering); + } + wcsncpyz(DestName,ResultName,DestSize); +} + +#endif
\ No newline at end of file diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/extract.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/extract.hpp new file mode 100644 index 00000000..159759b5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/extract.hpp @@ -0,0 +1,67 @@ +#ifndef _RAR_EXTRACT_ +#define _RAR_EXTRACT_ + +enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; + +class CmdExtract +{ + private: + EXTRACT_ARC_CODE ExtractArchive(); + bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); +#ifdef RARDLL + bool ExtrDllGetPassword(); +#else + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); +#endif +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); +#endif + void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); + bool ExtrCreateFile(Archive &Arc,File &CurFile); + bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); +#ifndef SFX_MODULE + bool DetectStartVolume(const wchar *VolName,bool NewNumbering); + void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); +#endif + + RarTime StartTime; // Time when extraction started. + + CommandData *Cmd; + + ComprDataIO DataIO; + Unpack *Unp; + unsigned long TotalFileCount; + + unsigned long FileCount; + unsigned long MatchedArgs; + bool FirstFile; + bool AllMatchesExact; + bool ReconstructDone; + bool UseExactVolName; + + // If any non-zero solid file was successfully unpacked before current. + // If true and if current encrypted file is broken, obviously + // the password is correct and we can report broken CRC without + // any wrong password hints. + bool AnySolidDataUnpackedWell; + + wchar ArcName[NM]; + + bool GlobalPassword; + bool PrevProcessed; // If previous file was successfully extracted or tested. + wchar DestFileName[NM]; + bool PasswordCancelled; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + bool Fat32,NotFat32; +#endif + public: + CmdExtract(CommandData *Cmd); + ~CmdExtract(); + void DoExtract(); + void ExtractArchiveInit(Archive &Arc); + bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat); + static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.cpp new file mode 100644 index 00000000..bcc2cb1e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.cpp @@ -0,0 +1,167 @@ +#include "rar.hpp" + +// If NewFile==NULL, we delete created file after user confirmation. +// It is useful if we need to overwrite an existing folder or file, +// but need user confirmation for that. +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) +{ + return true; // OPENMPT ADDITION + if (UserReject!=NULL) + *UserReject=false; +#ifdef _WIN_ALL + bool ShortNameChanged=false; +#endif + while (FileExist(Name)) + { +#if defined(_WIN_ALL) + if (!ShortNameChanged) + { + // Avoid the infinite loop if UpdateExistingShortName returns + // the same name. + ShortNameChanged=true; + + // Maybe our long name matches the short name of existing file. + // Let's check if we can change the short name. + if (UpdateExistingShortName(Name)) + continue; + } + // Allow short name check again. It is necessary, because rename and + // autorename below can change the name, so we need to check it again. + ShortNameChanged=false; +#endif + UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); + + if (Choice==UIASKREP_R_REPLACE) + break; + if (Choice==UIASKREP_R_SKIP) + { + if (UserReject!=NULL) + *UserReject=true; + return false; + } + if (Choice==UIASKREP_R_CANCEL) + ErrHandler.Exit(RARX_USERBREAK); + } + + // Try to truncate the existing file first instead of delete, + // so we preserve existing file permissions, such as NTFS permissions, + // also as "Compressed" attribute and hard links. In GUI version we avoid + // deleting an existing file for non-.rar archive formats as well. + uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; + if (NewFile!=NULL && NewFile->Create(Name,FileMode)) + return true; + + CreatePath(Name,true,Cmd->DisableNames); + return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); +} + + +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) +{ + return true; // OPENMPT ADDITION + wchar NewName[NM]; + size_t NameLength=wcslen(Name); + wchar *Ext=GetExt(Name); + if (Ext==NULL) + Ext=Name+NameLength; + for (uint FileVer=1;;FileVer++) + { + swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); + if (!FileExist(NewName)) + { + wcsncpyz(Name,NewName,MaxNameSize); + break; + } + if (FileVer>=1000000) + return false; + } + return true; +} + + +#if defined(_WIN_ALL) +// If we find a file, which short name is equal to 'Name', we try to change +// its short name, while preserving the long name. It helps when unpacking +// an archived file, which long name is equal to short name of already +// existing file. Otherwise we would overwrite the already existing file, +// even though its long name does not match the name of unpacking file. +bool UpdateExistingShortName(const wchar *Name) +{ + wchar LongPathName[NM]; + DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); + if (Res==0 || Res>=ASIZE(LongPathName)) + return false; + wchar ShortPathName[NM]; + Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); + if (Res==0 || Res>=ASIZE(ShortPathName)) + return false; + wchar *LongName=PointToName(LongPathName); + wchar *ShortName=PointToName(ShortPathName); + + // We continue only if file has a short name, which does not match its + // long name, and this short name is equal to name of file which we need + // to create. + if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || + wcsicomp(PointToName(Name),ShortName)!=0) + return false; + + // Generate the temporary new name for existing file. + wchar NewName[NM]; + *NewName=0; + for (int I=0;I<10000 && *NewName==0;I+=123) + { + // Here we copy the path part of file to create. We'll make the temporary + // file in the same folder. + wcsncpyz(NewName,Name,ASIZE(NewName)); + + // Here we set the random name part. + swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + + // If such file is already exist, try next random name. + if (FileExist(NewName)) + *NewName=0; + } + + // If we could not generate the name not used by any other file, we return. + if (*NewName==0) + return false; + + // FastFind returns the name without path, but we need the fully qualified + // name for renaming, so we use the path from file to create and long name + // from existing file. + wchar FullName[NM]; + wcsncpyz(FullName,Name,ASIZE(FullName)); + SetName(FullName,LongName,ASIZE(FullName)); + + // Rename the existing file to randomly generated name. Normally it changes + // the short name too. + if (!MoveFile(FullName,NewName)) + return false; + + // Now we need to create the temporary empty file with same name as + // short name of our already existing file. We do it to occupy its previous + // short name and not allow to use it again when renaming the file back to + // its original long name. + File KeepShortFile; + bool Created=false; + if (!FileExist(Name)) + Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); + + // Now we rename the existing file from temporary name to original long name. + // Since its previous short name is occupied by another file, it should + // get another short name. + MoveFile(NewName,FullName); + + if (Created) + { + // Delete the temporary zero length file occupying the short name, + KeepShortFile.Close(); + KeepShortFile.Delete(); + } + // We successfully changed the short name. Maybe sometimes we'll simplify + // this function by use of SetFileShortName Windows API call. + // But SetFileShortName is not available in older Windows. + return true; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.hpp new file mode 100644 index 00000000..44f801d4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filcreat.hpp @@ -0,0 +1,14 @@ +#ifndef _RAR_FILECREATE_ +#define _RAR_FILECREATE_ + +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize=INT64NDF, + RarTime *FileTime=NULL,bool WriteOnly=false); + +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); + +#if defined(_WIN_ALL) +bool UpdateExistingShortName(const wchar *Name); +#endif + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/file.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/file.cpp new file mode 100644 index 00000000..5be911da --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/file.cpp @@ -0,0 +1,828 @@ +#include "rar.hpp" + +File::File() +{ + hFile=FILE_BAD_HANDLE; + *FileName=0; + NewFile=false; + LastWrite=false; + HandleType=FILE_HANDLENORMAL; + LineInput=false; + SkipClose=false; + ErrorType=FILE_SUCCESS; + OpenShared=false; + AllowDelete=true; + AllowExceptions=true; + PreserveAtime=false; +#ifdef _WIN_ALL + CreateMode=FMF_UNDEFINED; +#endif + ReadErrorMode=FREM_ASK; + TruncatedAfterReadError=false; + CurFilePos=0; +} + + +File::~File() +{ + if (hFile!=FILE_BAD_HANDLE && !SkipClose) + if (NewFile) + Delete(); + else + Close(); +} + + +void File::operator = (File &SrcFile) +{ + hFile=SrcFile.hFile; + NewFile=SrcFile.NewFile; + LastWrite=SrcFile.LastWrite; + HandleType=SrcFile.HandleType; + TruncatedAfterReadError=SrcFile.TruncatedAfterReadError; + wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); + SrcFile.SkipClose=true; +} + + +bool File::Open(const wchar *Name,uint Mode) +{ + ErrorType=FILE_SUCCESS; + FileHandle hNewFile; + bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; + bool UpdateMode=(Mode & FMF_UPDATE)!=0; + bool WriteMode=(Mode & FMF_WRITE)!=0; +#ifdef _WIN_ALL + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; + if (UpdateMode) + Access|=GENERIC_WRITE; + uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; + if (OpenShared) + ShareMode|=FILE_SHARE_WRITE; + uint Flags=FILE_FLAG_SEQUENTIAL_SCAN; + FindData FD; + if (PreserveAtime) + Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. + hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + DWORD LastError; + if (hNewFile==FILE_BAD_HANDLE) + { + LastError=GetLastError(); + + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + { + hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + // For archive names longer than 260 characters first CreateFile + // (without \\?\) fails and sets LastError to 3 (access denied). + // We need the correct "file not found" error code to decide + // if we create a new archive or quit with "cannot create" error. + // So we need to check the error code after \\?\ CreateFile again, + // otherwise we'll fail to create new archives with long names. + // But we cannot simply assign the new code to LastError, + // because it would break "..\arcname.rar" relative names processing. + // First CreateFile returns the correct "file not found" code for such + // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating + // dots as a directory name. So we check only for "file not found" + // error here and for other errors use the first CreateFile result. + if (GetLastError()==ERROR_FILE_NOT_FOUND) + LastError=ERROR_FILE_NOT_FOUND; + } + } + if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) + ErrorType=FILE_NOTFOUND; + if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE) + { + FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification. + SetFileTime(hNewFile,NULL,&ft,NULL); + } + +#else + int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); +#ifdef O_BINARY + flags|=O_BINARY; +#if defined(_AIX) && defined(_LARGE_FILE_API) + flags|=O_LARGEFILE; +#endif +#endif + // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+. +#if defined(O_NOATIME) + if (PreserveAtime) + flags|=O_NOATIME; +#endif + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + + int handle=open(NameA,flags); +#ifdef LOCK_EX + +#ifdef _OSF_SOURCE + extern "C" int flock(int, int); +#endif + if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) + { + close(handle); + return false; + } + +#endif + if (handle==-1) + hNewFile=FILE_BAD_HANDLE; + else + { +#ifdef FILE_USE_OPEN + hNewFile=handle; +#else + hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); +#endif + } + if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT) + ErrorType=FILE_NOTFOUND; +#endif + NewFile=false; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + bool Success=hNewFile!=FILE_BAD_HANDLE; + if (Success) + { + hFile=hNewFile; + wcsncpyz(FileName,Name,ASIZE(FileName)); + TruncatedAfterReadError=false; + } + return Success; +} + + +#if !defined(SFX_MODULE) +void File::TOpen(const wchar *Name) +{ + if (!WOpen(Name)) + ErrHandler.Exit(RARX_OPEN); +} +#endif + + +bool File::WOpen(const wchar *Name) +{ + if (Open(Name)) + return true; + ErrHandler.OpenErrorMsg(Name); + return false; +} + + +bool File::Create(const wchar *Name,uint Mode) +{ + return false; /* // OPENMPT ADDITION + // OpenIndiana based NAS and CIFS shares fail to set the file time if file + // was created in read+write mode and some data was written and not flushed + // before SetFileTime call. So we should use the write only mode if we plan + // SetFileTime call and do not need to read from file. + bool WriteMode=(Mode & FMF_WRITE)!=0; + bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; +#ifdef _WIN_ALL + CreateMode=Mode; + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; + DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; + + // Windows automatically removes dots and spaces in the end of file name, + // So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + + if (Special && (Mode & FMF_STANDARDNAMES)==0) + hFile=FILE_BAD_HANDLE; + else + hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + + if (hFile==FILE_BAD_HANDLE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + } + +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); +#ifdef FILE_USE_OPEN + hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); +#else + hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); +#endif +#endif + NewFile=true; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + wcsncpyz(FileName,Name,ASIZE(FileName)); + return hFile!=FILE_BAD_HANDLE; + */ // OPENMPT ADDITION +} + + +#if !defined(SFX_MODULE) +void File::TCreate(const wchar *Name,uint Mode) +{ + if (!WCreate(Name,Mode)) + ErrHandler.Exit(RARX_FATAL); +} +#endif + + +bool File::WCreate(const wchar *Name,uint Mode) +{ + if (Create(Name,Mode)) + return true; + ErrHandler.CreateErrorMsg(Name); + return false; +} + + +bool File::Close() +{ + bool Success=true; + + if (hFile!=FILE_BAD_HANDLE) + { + if (!SkipClose) + { +#ifdef _WIN_ALL + // We use the standard system handle for stdout in Windows + // and it must not be closed here. + if (HandleType==FILE_HANDLENORMAL) + Success=CloseHandle(hFile)==TRUE; +#else +#ifdef FILE_USE_OPEN + Success=close(hFile)!=-1; +#else + Success=fclose(hFile)!=EOF; +#endif +#endif + } + hFile=FILE_BAD_HANDLE; + } + HandleType=FILE_HANDLENORMAL; + if (!Success && AllowExceptions) + ErrHandler.CloseError(FileName); + return Success; +} + + +bool File::Delete() +{ + if (HandleType!=FILE_HANDLENORMAL) + return false; + if (hFile!=FILE_BAD_HANDLE) + Close(); + if (!AllowDelete) + return false; + return DelFile(FileName); +} + + +bool File::Rename(const wchar *NewName) +{ + // No need to rename if names are already same. + bool Success=wcscmp(FileName,NewName)==0; + + if (!Success) + Success=RenameFile(FileName,NewName); + + if (Success) + wcsncpyz(FileName,NewName,ASIZE(FileName)); + + return Success; +} + + +bool File::Write(const void *Data,size_t Size) +{ + return true; /* // OPENMPT ADDITION + if (Size==0) + return true; + if (HandleType==FILE_HANDLESTD) + { +#ifdef _WIN_ALL + hFile=GetStdHandle(STD_OUTPUT_HANDLE); +#else + // Cannot use the standard stdout here, because it already has wide orientation. + if (hFile==FILE_BAD_HANDLE) + { +#ifdef FILE_USE_OPEN + hFile=dup(STDOUT_FILENO); // Open new stdout stream. +#else + hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream. +#endif + } +#endif + } + bool Success; + while (1) + { + Success=false; +#ifdef _WIN_ALL + DWORD Written=0; + if (HandleType!=FILE_HANDLENORMAL) + { + // writing to stdout can fail in old Windows if data block is too large + const size_t MaxSize=0x4000; + for (size_t I=0;I<Size;I+=MaxSize) + { + Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE; + if (!Success) + break; + } + } + else + Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE; +#else +#ifdef FILE_USE_OPEN + ssize_t Written=write(hFile,Data,Size); + Success=Written==Size; +#else + int Written=fwrite(Data,1,Size,hFile); + Success=Written==Size && !ferror(hFile); +#endif +#endif + if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL) + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL) + int ErrCode=GetLastError(); + int64 FilePos=Tell(); + uint64 FreeSize=GetFreeDisk(FileName); + SetLastError(ErrCode); + if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff) + ErrHandler.WriteErrorFAT(FileName); +#endif + if (ErrHandler.AskRepeatWrite(FileName,false)) + { +#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN) + clearerr(hFile); +#endif + if (Written<Size && Written>0) + Seek(Tell()-Written,SEEK_SET); + continue; + } + ErrHandler.WriteError(NULL,FileName); + } + break; + } + LastWrite=true; + return Success; // It can return false only if AllowExceptions is disabled. + */ // OPENMPT ADDITION +} + + +int File::Read(void *Data,size_t Size) +{ + if (TruncatedAfterReadError) + return 0; + + int64 FilePos=0; // Initialized only to suppress some compilers warning. + + if (ReadErrorMode==FREM_IGNORE) + FilePos=Tell(); + int TotalRead=0; + while (true) + { + int ReadSize=DirectRead(Data,Size); + + if (ReadSize==-1) + { + ErrorType=FILE_READERROR; + if (AllowExceptions) + if (ReadErrorMode==FREM_IGNORE) + { + ReadSize=0; + for (size_t I=0;I<Size;I+=512) + { + Seek(FilePos+I,SEEK_SET); + size_t SizeToRead=Min(Size-I,512); + int ReadCode=DirectRead(Data,SizeToRead); + ReadSize+=(ReadCode==-1) ? 512:ReadCode; + if (ReadSize!=-1) + TotalRead+=ReadSize; + } + } + else + { + bool Ignore=false,Retry=false,Quit=false; + if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL) + { + ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit); + if (Retry) + continue; + } + if (Ignore || ReadErrorMode==FREM_TRUNCATE) + { + TruncatedAfterReadError=true; + return 0; + } + ErrHandler.ReadError(FileName); + } + } + TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here. + + if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size) + { + // Unlike regular files, for pipe we can read only as much as was + // written at the other end of pipe. We had seen data coming in small + // ~80 byte chunks when piping from 'type arc.rar'. Extraction code + // would fail if we read an incomplete archive header from stdin. + // So here we ensure that requested size is completely read. + // But we return the available data immediately in "line input" mode, + // when processing user's input in console prompts. Otherwise apps + // piping user responses to multiple Ask() prompts can hang if no more + // data is available yet and pipe isn't closed. + Data=(byte*)Data+ReadSize; + Size-=ReadSize; + continue; + } + break; + } + if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled. + CurFilePos+=TotalRead; + return TotalRead; // It can return -1 only if AllowExceptions is disabled. +} + + +// Returns -1 in case of error. +int File::DirectRead(void *Data,size_t Size) +{ +#ifdef _WIN_ALL + const size_t MaxDeviceRead=20000; + const size_t MaxLockedRead=32768; +#endif + if (HandleType==FILE_HANDLESTD) + { +#ifdef _WIN_ALL +// if (Size>MaxDeviceRead) +// Size=MaxDeviceRead; + hFile=GetStdHandle(STD_INPUT_HANDLE); +#else +#ifdef FILE_USE_OPEN + hFile=STDIN_FILENO; +#else + hFile=stdin; +#endif +#endif + } +#ifdef _WIN_ALL + // For pipes like 'type file.txt | rar -si arcname' ReadFile may return + // data in small ~4KB blocks. It may slightly reduce the compression ratio. + DWORD Read; + if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) + { + if (IsDevice() && Size>MaxDeviceRead) + return DirectRead(Data,MaxDeviceRead); + if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) + return 0; + + // We had a bug report about failure to archive 1C database lock file + // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB + // permanently locked. If our first read request uses too large buffer + // and if we are in -dh mode, so we were able to open the file, + // we'll fail with "Read error". So now we use try a smaller buffer size + // in case of lock error. + if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead && + GetLastError()==ERROR_LOCK_VIOLATION) + return DirectRead(Data,MaxLockedRead); + + return -1; + } + return Read; +#else +#ifdef FILE_USE_OPEN + ssize_t ReadSize=read(hFile,Data,Size); + if (ReadSize==-1) + return -1; + return (int)ReadSize; +#else + if (LastWrite) + { + fflush(hFile); + LastWrite=false; + } + clearerr(hFile); + size_t ReadSize=fread(Data,1,Size,hFile); + if (ferror(hFile)) + return -1; + return (int)ReadSize; +#endif +#endif +} + + +void File::Seek(int64 Offset,int Method) +{ + if (!RawSeek(Offset,Method) && AllowExceptions) + ErrHandler.SeekError(FileName); +} + + +bool File::RawSeek(int64 Offset,int Method) +{ + if (hFile==FILE_BAD_HANDLE) + return true; + if (!IsSeekable()) + { + if (Method==SEEK_CUR) + { + Offset+=CurFilePos; + Method=SEEK_SET; + } + if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward. + { + uint64 SkipSize=Offset-CurFilePos; + while (SkipSize>0) + { + byte Buf[4096]; + int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); + if (ReadSize<=0) + return false; + SkipSize-=ReadSize; + } + CurFilePos=Offset; + return true; + } + + return false; // Backward or end of file seek on unseekable file. + } + if (Offset<0 && Method!=SEEK_SET) + { + Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; + Method=SEEK_SET; + } +#ifdef _WIN_ALL + LONG HighDist=(LONG)(Offset>>32); + if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && + GetLastError()!=NO_ERROR) + return false; +#else + LastWrite=false; +#ifdef FILE_USE_OPEN + if (lseek(hFile,(off_t)Offset,Method)==-1) + return false; +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) + if (fseeko(hFile,Offset,Method)!=0) + return false; +#else + if (fseek(hFile,(long)Offset,Method)!=0) + return false; +#endif +#endif + return true; +} + + +int64 File::Tell() +{ + if (hFile==FILE_BAD_HANDLE) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; + if (!IsSeekable()) + return CurFilePos; +#ifdef _WIN_ALL + LONG HighDist=0; + uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); + if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; + return INT32TO64(HighDist,LowDist); +#else +#ifdef FILE_USE_OPEN + return lseek(hFile,0,SEEK_CUR); +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) + return ftello(hFile); +#else + return ftell(hFile); +#endif +#endif +} + + +void File::Prealloc(int64 Size) +{ + /* // OPENMPT ADDITION +#ifdef _WIN_ALL + if (RawSeek(Size,SEEK_SET)) + { + Truncate(); + Seek(0,SEEK_SET); + } +#endif + +#if defined(_UNIX) && defined(USE_FALLOCATE) + // fallocate is rather new call. Only latest kernels support it. + // So we are not using it by default yet. + int fd = GetFD(); + if (fd >= 0) + fallocate(fd, 0, 0, Size); +#endif + */ // OPENMPT ADDITION +} + + +byte File::GetByte() +{ + byte Byte=0; + Read(&Byte,1); + return Byte; +} + + +void File::PutByte(byte Byte) +{ + Write(&Byte,1); +} + + +bool File::Truncate() +{ + return false; /* // OPENMPT ADDITION +#ifdef _WIN_ALL + return SetEndOfFile(hFile)==TRUE; +#else + return ftruncate(GetFD(),(off_t)Tell())==0; +#endif + */ // OPENMPT ADDITION +} + + +void File::Flush() +{ + return; /* // OPENMPT ADDITION +#ifdef _WIN_ALL + FlushFileBuffers(hFile); +#else +#ifndef FILE_USE_OPEN + fflush(hFile); +#endif + fsync(GetFD()); +#endif + */ // OPENMPT EDDITION +} + + +void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) +{ + /* // OPENMPT ADDITION +#ifdef _WIN_ALL + // Workaround for OpenIndiana NAS time bug. If we cannot create a file + // in write only mode, we need to flush the write buffer before calling + // SetFileTime or file time will not be changed. + if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + FlushFileBuffers(hFile); + + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + FILETIME fm,fc,fa; + if (sm) + ftm->GetWinFT(&fm); + if (sc) + ftc->GetWinFT(&fc); + if (sa) + fta->GetWinFT(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); +#endif + */ // OPENMPT ADDITION +} + + +void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) +{ + /* // OPENMPT ADDITION +// Android APP_PLATFORM := android-14 does not support futimens and futimes. +// Newer platforms support futimens, but fail on Android 4.2. +// We have to use utime for Android. +// Also we noticed futimens fail to set timestamps on NTFS partition +// mounted to virtual Linux x86 machine, but utimensat worked correctly. +// So we set timestamps for already closed files in Unix. +#ifdef _UNIX + SetCloseFileTimeByName(FileName,ftm,fta); +#endif + */ // OPENMPT ADDITION +} + + +void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) +{ + /* // OPENMPT ADDITION +#ifdef _UNIX + bool setm=ftm!=NULL && ftm->IsSet(); + bool seta=fta!=NULL && fta->IsSet(); + if (setm || seta) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=seta ? fta->GetUnix() : 0; + times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=setm ? ftm->GetUnix() : 0; + times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,NameA,times,0); +#else + utimbuf ut; + if (setm) + ut.modtime=ftm->GetUnix(); + else + ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0. + if (seta) + ut.actime=fta->GetUnix(); + else + ut.actime=ut.modtime; // Need to set something, cannot left it 0. + utime(NameA,&ut); +#endif + } +#endif + */ // OPENMPT ADDITION +} + + +void File::GetOpenFileTime(RarTime *ft) +{ + /* // OPENMPT ADDITION +#ifdef _WIN_ALL + FILETIME FileTime; + GetFileTime(hFile,NULL,NULL,&FileTime); + ft->SetWinFT(&FileTime); +#endif +#if defined(_UNIX) || defined(_EMX) + struct stat st; + fstat(GetFD(),&st); + ft->SetUnix(st.st_mtime); +#endif + */ // OPENMPT ADDITION +} + + +int64 File::FileLength() +{ + int64 SavePos=Tell(); + Seek(0,SEEK_END); + int64 Length=Tell(); + Seek(SavePos,SEEK_SET); + return Length; +} + + +bool File::IsDevice() +{ + if (hFile==FILE_BAD_HANDLE) + return false; +#ifdef _WIN_ALL + uint Type=GetFileType(hFile); + return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE; +#else + return isatty(GetFD()); +#endif +} + + +#ifndef SFX_MODULE +int64 File::Copy(File &Dest,int64 Length) +{ + Array<byte> Buffer(File::CopyBufferSize()); + int64 CopySize=0; + bool CopyAll=(Length==INT64NDF); + + while (CopyAll || Length>0) + { + Wait(); + size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); + byte *Buf=&Buffer[0]; + int ReadSize=Read(Buf,SizeToRead); + if (ReadSize==0) + break; + size_t WriteSize=ReadSize; +#ifdef _WIN_ALL + // For FAT32 USB flash drives in Windows if first write is 4 KB or more, + // write caching is disabled and "write through" is enabled, resulting + // in bad performance, especially for many small files. It happens when + // we create SFX archive on USB drive, because SFX module is written first. + // So we split the first write to small 1 KB followed by rest of data. + if (CopySize==0 && WriteSize>=4096) + { + const size_t FirstWrite=1024; + Dest.Write(Buf,FirstWrite); + Buf+=FirstWrite; + WriteSize-=FirstWrite; + } +#endif + Dest.Write(Buf,WriteSize); + CopySize+=ReadSize; + if (!CopyAll) + Length-=ReadSize; + } + return CopySize; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/file.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/file.hpp new file mode 100644 index 00000000..1de5fe6d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/file.hpp @@ -0,0 +1,161 @@ +#ifndef _RAR_FILE_ +#define _RAR_FILE_ + +#define FILE_USE_OPEN + +#ifdef _WIN_ALL + typedef HANDLE FileHandle; + #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE +#elif defined(FILE_USE_OPEN) + typedef off_t FileHandle; + #define FILE_BAD_HANDLE -1 +#else + typedef FILE* FileHandle; + #define FILE_BAD_HANDLE NULL +#endif + +class RAROptions; + +enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; + +enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; + +enum FILE_MODE_FLAGS { + // Request read only access to file. Default for Open. + FMF_READ=0, + + // Request both read and write access to file. Default for Create. + FMF_UPDATE=1, + + // Request write only access to file. + FMF_WRITE=2, + + // Open files which are already opened for write by other programs. + FMF_OPENSHARED=4, + + // Open files only if no other program is opened it even in shared mode. + FMF_OPENEXCLUSIVE=8, + + // Provide read access to created file for other programs. + FMF_SHAREREAD=16, + + // Use standard NTFS names without trailing dots and spaces. + FMF_STANDARDNAMES=32, + + // Mode flags are not defined yet. + FMF_UNDEFINED=256 +}; + +enum FILE_READ_ERROR_MODE { + FREM_ASK, // Propose to use the already read part, retry or abort. + FREM_TRUNCATE, // Use the already read part without additional prompt. + FREM_IGNORE // Try to skip unreadable block and read further. +}; + + +class File +{ + private: + FileHandle hFile; + bool LastWrite; + FILE_HANDLETYPE HandleType; + + // If we read the user input in console prompts from stdin, we shall + // process the available line immediately, not waiting for rest of data. + // Otherwise apps piping user responses to multiple Ask() prompts can + // hang if no more data is available yet and pipe isn't closed. + // If we read RAR archive or other file data from stdin, we shall collect + // the entire requested block as long as pipe isn't closed, so we get + // complete archive headers, not split between different reads. + bool LineInput; + + bool SkipClose; + FILE_READ_ERROR_MODE ReadErrorMode; + bool NewFile; + bool AllowDelete; + bool AllowExceptions; +#ifdef _WIN_ALL + bool NoSequentialRead; + uint CreateMode; +#endif + bool PreserveAtime; + bool TruncatedAfterReadError; + + int64 CurFilePos; // Used for forward seeks in stdin files. + protected: + bool OpenShared; // Set by 'Archive' class. + public: + wchar FileName[NM]; + + FILE_ERRORTYPE ErrorType; + public: + File(); + virtual ~File(); + void operator = (File &SrcFile); + + // Several functions below are 'virtual', because they are redefined + // by Archive for QOpen and by MultiFile for split files in WinRAR. + virtual bool Open(const wchar *Name,uint Mode=FMF_READ); + void TOpen(const wchar *Name); + bool WOpen(const wchar *Name); + bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + virtual bool Close(); // 'virtual' for MultiFile class. + bool Delete(); + bool Rename(const wchar *NewName); + bool Write(const void *Data,size_t Size); + virtual int Read(void *Data,size_t Size); + int DirectRead(void *Data,size_t Size); + virtual void Seek(int64 Offset,int Method); + bool RawSeek(int64 Offset,int Method); + virtual int64 Tell(); + void Prealloc(int64 Size); + byte GetByte(); + void PutByte(byte Byte); + bool Truncate(); + void Flush(); + void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); + void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); + static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); + void GetOpenFileTime(RarTime *ft); + virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. + int64 FileLength(); + void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} + void SetLineInputMode(bool Mode) {LineInput=Mode;} + FILE_HANDLETYPE GetHandleType() {return HandleType;} + bool IsSeekable() {return HandleType!=FILE_HANDLESTD;} + bool IsDevice(); + static bool RemoveCreated(); + FileHandle GetHandle() {return hFile;} + void SetHandle(FileHandle Handle) {Close();hFile=Handle;} + void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;} + int64 Copy(File &Dest,int64 Length=INT64NDF); + void SetAllowDelete(bool Allow) {AllowDelete=Allow;} + void SetExceptions(bool Allow) {AllowExceptions=Allow;} + void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;} + bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;} +#ifdef _UNIX + int GetFD() + { +#ifdef FILE_USE_OPEN + return hFile; +#else + return fileno(hFile); +#endif + } +#endif + static size_t CopyBufferSize() + { +#ifdef _WIN_ALL + // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it. + // For copying from HDD to same HDD the best performance was with 256 KB + // buffer in XP and with 1 MB buffer in Win10. + return WinNT()==WNT_WXP ? 0x40000:0x100000; +#else + return 0x100000; +#endif + } +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.cpp new file mode 100644 index 00000000..c985b777 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.cpp @@ -0,0 +1,560 @@ +#include "rar.hpp" + +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) +{ + return MKDIR_SUCCESS; // OPENMPT ADDITION +#ifdef _WIN_ALL + // Windows automatically removes dots and spaces in the end of directory + // name. So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); + if (RetCode==0 && !FileExist(Name)) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + RetCode=CreateDirectory(LongName,NULL); + } + if (RetCode!=0) // Non-zero return code means success for CreateDirectory. + { + if (SetAttr) + SetFileAttr(Name,Attr); + return MKDIR_SUCCESS; + } + int ErrCode=GetLastError(); + if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) + return MKDIR_BADPATH; + return MKDIR_ERROR; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + mode_t uattr=SetAttr ? (mode_t)Attr:0777; + int ErrCode=mkdir(NameA,uattr); + if (ErrCode==-1) + return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; + return MKDIR_SUCCESS; +#else + return MKDIR_ERROR; +#endif +} + + +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) +{ + return true; // OPENMPT ADDITION + if (Path==NULL || *Path==0) + return false; + +#if defined(_WIN_ALL) || defined(_EMX) + uint DirAttr=0; +#else + uint DirAttr=0777; +#endif + + bool Success=true; + + for (const wchar *s=Path;*s!=0;s++) + { + wchar DirName[NM]; + if (s-Path>=ASIZE(DirName)) + break; + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. s>Path check avoids attempting + // creating an empty directory for paths starting from path separator. + if (IsPathDiv(*s) && s>Path) + { +#ifdef _WIN_ALL + // We must not attempt to create "D:" directory, because first + // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine + // to create "D:" directory. + if (s==Path+2 && Path[1]==':') + continue; +#endif + wcsncpy(DirName,Path,s-Path); + DirName[s-Path]=0; + + Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; + if (Success && !Silent) + { + mprintf(St(MCreatDir),DirName); + mprintf(L" %s",St(MOk)); + } + } + } + if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) + Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; + return Success; +} + + +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) +{ + return; // OPENMPT ADDITION +#if defined(_WIN_ALL) + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + + uint DirAttr=GetFileAttr(Name); + bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); + if (ResetAttr) + SetFileAttr(Name,0); + + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + } + + if (hFile==INVALID_HANDLE_VALUE) + return; + FILETIME fm,fc,fa; + if (sm) + ftm->GetWinFT(&fm); + if (sc) + ftc->GetWinFT(&fc); + if (sa) + fta->GetWinFT(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); + CloseHandle(hFile); + if (ResetAttr) + SetFileAttr(Name,DirAttr); +#endif +#if defined(_UNIX) || defined(_EMX) + File::SetCloseFileTimeByName(Name,ftm,fta); +#endif +} + + +bool IsRemovable(const wchar *Name) +{ + return false; // OPENMPT ADDITION +#if defined(_WIN_ALL) + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + int Type=GetDriveType(*Root!=0 ? Root:NULL); + return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; +#else + return false; +#endif +} + + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name) +{ + return 0; // OPENMPT ADDITION +#ifdef _WIN_ALL + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + + ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; + uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; + if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) + return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); + return 0; +#elif defined(_UNIX) + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + char RootA[NM]; + WideToChar(Root,RootA,ASIZE(RootA)); + struct statvfs sfs; + if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) + return 0; + int64 FreeSize=sfs.f_bsize; + FreeSize=FreeSize*sfs.f_bavail; + return FreeSize; +#else + return 0; +#endif +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +// Return 'true' for FAT and FAT32, so we can adjust the maximum supported +// file size to 4 GB for these file systems. +bool IsFAT(const wchar *Name) +{ + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + wchar FileSystem[MAX_PATH+1]; + if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) + return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; + return false; +} +#endif + + +bool FileExist(const wchar *Name) +{ + return false; // OPENMPT ADDITION +#ifdef _WIN_ALL + return GetFileAttr(Name)!=0xffffffff; +#elif defined(ENABLE_ACCESS) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return access(NameA,0)==0; +#else + FindData FD; + return FindFile::FastFind(Name,&FD); +#endif +} + + +bool WildFileExist(const wchar *Name) +{ + return false; // OPENMPT ADDITION + if (IsWildcard(Name)) + { + FindFile Find; + Find.SetMask(Name); + FindData fd; + return Find.Next(&fd); + } + return FileExist(Name); +} + + +bool IsDir(uint Attr) +{ +#ifdef _WIN_ALL + return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; +#endif +#if defined(_UNIX) + return (Attr & 0xF000)==0x4000; +#endif +} + + +bool IsUnreadable(uint Attr) +{ +#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) + return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); +#endif + return false; +} + + +bool IsLink(uint Attr) +{ +#ifdef _UNIX + return (Attr & 0xF000)==0xA000; +#elif defined(_WIN_ALL) + return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; +#else + return false; +#endif +} + + + + + + +bool IsDeleteAllowed(uint FileAttr) +{ +#ifdef _WIN_ALL + return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; +#else + return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); +#endif +} + + +void PrepareToDelete(const wchar *Name) +{ + return; // OPENMPT ADDITION +#if defined(_WIN_ALL) || defined(_EMX) + SetFileAttr(Name,0); +#endif +#ifdef _UNIX + if (Name!=NULL) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); + } +#endif +} + + +uint GetFileAttr(const wchar *Name) +{ + return 0; // OPENMPT ADDITION +#ifdef _WIN_ALL + DWORD Attr=GetFileAttributes(Name); + if (Attr==0xffffffff) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Attr=GetFileAttributes(LongName); + } + return Attr; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + struct stat st; + if (stat(NameA,&st)!=0) + return 0; + return st.st_mode; +#endif +} + + +bool SetFileAttr(const wchar *Name,uint Attr) +{ + return false; // OPENMPT ADDITION +#ifdef _WIN_ALL + bool Success=SetFileAttributes(Name,Attr)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=SetFileAttributes(LongName,Attr)!=0; + } + return Success; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return chmod(NameA,(mode_t)Attr)==0; +#else + return false; +#endif +} + + +#if 0 +wchar *MkTemp(wchar *Name,size_t MaxSize) +{ + size_t Length=wcslen(Name); + + RarTime CurTime; + CurTime.SetCurrentTime(); + + // We cannot use CurTime.GetWin() as is, because its lowest bits can + // have low informational value, like being a zero or few fixed numbers. + uint Random=(uint)(CurTime.GetWin()/100000); + + // Using PID we guarantee that different RAR copies use different temp names + // even if started in exactly the same time. + uint PID=0; +#ifdef _WIN_ALL + PID=(uint)GetCurrentProcessId(); +#elif defined(_UNIX) + PID=(uint)getpid(); +#endif + + for (uint Attempt=0;;Attempt++) + { + uint Ext=Random%50000+Attempt; + wchar RndText[50]; + swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); + if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) + return NULL; + wcsncpyz(Name+Length,RndText,MaxSize-Length); + if (!FileExist(Name)) + break; + } + return Name; +} +#endif + + +#if !defined(SFX_MODULE) +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) +{ + int64 SavePos=SrcFile->Tell(); +#ifndef SILENT + int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; +#endif + + if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) + uiMsg(UIEVENT_FILESUMSTART); + + if ((Flags & CALCFSUM_CURPOS)==0) + SrcFile->Seek(0,SEEK_SET); + + const size_t BufSize=0x100000; + Array<byte> Data(BufSize); + + + DataHash HashCRC,HashBlake2; + HashCRC.Init(HASH_CRC32,Threads); + HashBlake2.Init(HASH_BLAKE2,Threads); + + int64 BlockCount=0; + int64 TotalRead=0; + while (true) + { + size_t SizeToRead; + if (Size==INT64NDF) // If we process the entire file. + SizeToRead=BufSize; // Then always attempt to read the entire buffer. + else + SizeToRead=(size_t)Min((int64)BufSize,Size); + int ReadSize=SrcFile->Read(&Data[0],SizeToRead); + if (ReadSize==0) + break; + TotalRead+=ReadSize; + + if ((++BlockCount & 0xf)==0) + { +#ifndef SILENT + if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) + { + // Update only the current file progress in WinRAR, set the total to 0 + // to keep it as is. It looks better for WinRAR, + uiExtractProgress(TotalRead,FileLength,0,0); + } + else + { + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); + } +#endif + Wait(); + } + + if (CRC32!=NULL) + HashCRC.Update(&Data[0],ReadSize); + if (Blake2!=NULL) + HashBlake2.Update(&Data[0],ReadSize); + + if (Size!=INT64NDF) + Size-=ReadSize; + } + SrcFile->Seek(SavePos,SEEK_SET); + + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMEND); + + if (CRC32!=NULL) + *CRC32=HashCRC.GetCRC32(); + if (Blake2!=NULL) + { + HashValue Result; + HashBlake2.Result(&Result); + memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); + } +} +#endif + + +bool RenameFile(const wchar *SrcName,const wchar *DestName) +{ + return true; // OPENMPT ADDITION +#ifdef _WIN_ALL + bool Success=MoveFile(SrcName,DestName)!=0; + if (!Success) + { + wchar LongName1[NM],LongName2[NM]; + if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && + GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) + Success=MoveFile(LongName1,LongName2)!=0; + } + return Success; +#else + char SrcNameA[NM],DestNameA[NM]; + WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); + WideToChar(DestName,DestNameA,ASIZE(DestNameA)); + bool Success=rename(SrcNameA,DestNameA)==0; + return Success; +#endif +} + + +bool DelFile(const wchar *Name) +{ + return true; // OPENMPT ADDITION +#ifdef _WIN_ALL + bool Success=DeleteFile(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=DeleteFile(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=remove(NameA)==0; + return Success; +#endif +} + + +bool DelDir(const wchar *Name) +{ + return true; // OPENMPT ADDITION +#ifdef _WIN_ALL + bool Success=RemoveDirectory(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=RemoveDirectory(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=rmdir(NameA)==0; + return Success; +#endif +} + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State) +{ + return true; // OPENMPT ADDITION + HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + } + if (hFile==INVALID_HANDLE_VALUE) + return false; + SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; + DWORD Result; + int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, + sizeof(NewState),NULL,0,&Result,NULL); + CloseHandle(hFile); + return RetCode!=0; +} + + +void ResetFileCache(const wchar *Name) +{ + // To reset file cache in Windows it is enough to open it with + // FILE_FLAG_NO_BUFFERING and then close it. + HANDLE hSrc=CreateFile(Name,GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); + if (hSrc!=INVALID_HANDLE_VALUE) + CloseHandle(hSrc); +} +#endif + + + + + + + + + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.hpp new file mode 100644 index 00000000..36dc7039 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filefn.hpp @@ -0,0 +1,51 @@ +#ifndef _RAR_FILEFN_ +#define _RAR_FILEFN_ + +enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; + +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent); +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const wchar *Name); + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name); +#endif + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +bool IsFAT(const wchar *Root); +#endif + +bool FileExist(const wchar *Name); +bool WildFileExist(const wchar *Name); +bool IsDir(uint Attr); +bool IsUnreadable(uint Attr); +bool IsLink(uint Attr); +void SetSFXMode(const wchar *FileName); +void EraseDiskContents(const wchar *FileName); +bool IsDeleteAllowed(uint FileAttr); +void PrepareToDelete(const wchar *Name); +uint GetFileAttr(const wchar *Name); +bool SetFileAttr(const wchar *Name,uint Attr); +#if 0 +wchar* MkTemp(wchar *Name,size_t MaxSize); +#endif + +enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; + +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); + +bool RenameFile(const wchar *SrcName,const wchar *DestName); +bool DelFile(const wchar *Name); +bool DelDir(const wchar *Name); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State); +void ResetFileCache(const wchar *Name); +#endif + + + + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.cpp new file mode 100644 index 00000000..a5d29d74 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.cpp @@ -0,0 +1,166 @@ +#include "rar.hpp" + +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError, + RAR_CHARSET SrcCharset, + bool Unquote, + bool SkipComments, + bool ExpandEnvStr) +{ + wchar FileName[NM]; + *FileName=0; + + if (Name!=NULL) + if (Config) + GetConfigName(Name,FileName,ASIZE(FileName),true,false); + else + wcsncpyz(FileName,Name,ASIZE(FileName)); + + File SrcFile; + if (*FileName!=0) + { + bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); + + if (!OpenCode) + { + if (AbortOnError) + ErrHandler.Exit(RARX_OPEN); + return false; + } + } + else + SrcFile.SetHandleType(FILE_HANDLESTD); + + uint DataSize=0,ReadSize; + const int ReadBlock=4096; + + Array<byte> Data(ReadBlock); + while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) + { + DataSize+=ReadSize; + Data.Add(ReadSize); // Always have ReadBlock available for next data. + } + // Set to really read size, so we can zero terminate it correctly. + Data.Alloc(DataSize); + + int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; + int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; + bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; + + if (SrcCharset==RCH_DEFAULT) + SrcCharset=DetectTextEncoding(&Data[0],DataSize); + + Array<wchar> DataW; + + if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) + { + Data.Push(0); // Zero terminate. +#if defined(_WIN_ALL) + if (SrcCharset==RCH_OEM) + OemToCharA((char *)&Data[0],(char *)&Data[0]); +#endif + DataW.Alloc(Data.Size()); + CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); + } + + if (SrcCharset==RCH_UNICODE) + { + size_t Start=2; // Skip byte order mark. + if (!LittleEndian && !BigEndian) // No byte order mask. + { + Start=0; + LittleEndian=1; + } + + DataW.Alloc(Data.Size()/2+1); + size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. + for (size_t I=Start;I<End;I+=2) + DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256; + DataW[(End-Start)/2]=0; + } + + if (SrcCharset==RCH_UTF8) + { + Data.Push(0); // Zero terminate data. + DataW.Alloc(Data.Size()); + UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size()); + } + + wchar *CurStr=&DataW[0]; + + while (*CurStr!=0) + { + wchar *NextStr=CurStr,*CmtPtr=NULL; + while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) + { + if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') + { + *NextStr=0; + CmtPtr=NextStr; + } + NextStr++; + } + bool Done=*NextStr==0; + + *NextStr=0; + for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + + if (Unquote && *CurStr=='\"') + { + size_t Length=wcslen(CurStr); + if (CurStr[Length-1]=='\"') + { + CurStr[Length-1]=0; + CurStr++; + } + } + + bool Expanded=false; +#if defined(_WIN_ALL) + if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows. + { + wchar ExpName[NM]; + *ExpName=0; + DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); + Expanded=Result!=0 && Result<ASIZE(ExpName); + if (Expanded && *ExpName!=0) + List->AddString(ExpName); + } +#endif + if (!Expanded && *CurStr!=0) + List->AddString(CurStr); + + if (Done) + break; + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; + } + return true; +} + + +RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize) +{ + if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf && + IsTextUtf8(Data+3,DataSize-3)) + return RCH_UTF8; + + bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254; + bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255; + + if (LittleEndian || BigEndian) + for (size_t I=LittleEndian ? 3 : 2;I<DataSize;I+=2) + if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n') + return RCH_UNICODE; // High byte in UTF-16 char is found. + + return RCH_DEFAULT; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.hpp new file mode 100644 index 00000000..febd0a29 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/filestr.hpp @@ -0,0 +1,17 @@ +#ifndef _RAR_FILESTR_ +#define _RAR_FILESTR_ + +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError=false, + RAR_CHARSET SrcCharset=RCH_DEFAULT, + bool Unquote=false, + bool SkipComments=false, + bool ExpandEnvStr=false +); + +RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/find.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/find.cpp new file mode 100644 index 00000000..499cdf45 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/find.cpp @@ -0,0 +1,226 @@ +#include "rar.hpp" + +FindFile::FindFile() +{ + *FindMask=0; + FirstCall=true; +#ifdef _WIN_ALL + hFind=INVALID_HANDLE_VALUE; +#else + dirp=NULL; +#endif +} + + +FindFile::~FindFile() +{ +#ifdef _WIN_ALL + if (hFind!=INVALID_HANDLE_VALUE) + FindClose(hFind); +#else + if (dirp!=NULL) + closedir(dirp); +#endif +} + + +void FindFile::SetMask(const wchar *Mask) +{ + wcsncpyz(FindMask,Mask,ASIZE(FindMask)); + FirstCall=true; +} + + +bool FindFile::Next(FindData *fd,bool GetSymLink) +{ + return true; // OPENMPT ADDITION + fd->Error=false; + if (*FindMask==0) + return false; +#ifdef _WIN_ALL + if (FirstCall) + { + if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE) + return false; + } + else + if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE) + return false; +#else + if (FirstCall) + { + wchar DirName[NM]; + wcsncpyz(DirName,FindMask,ASIZE(DirName)); + RemoveNameFromPath(DirName); + if (*DirName==0) + wcsncpyz(DirName,L".",ASIZE(DirName)); + char DirNameA[NM]; + WideToChar(DirName,DirNameA,ASIZE(DirNameA)); + if ((dirp=opendir(DirNameA))==NULL) + { + fd->Error=(errno!=ENOENT); + return false; + } + } + while (1) + { + wchar Name[NM]; + struct dirent *ent=readdir(dirp); + if (ent==NULL) + return false; + if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) + continue; + if (!CharToWide(ent->d_name,Name,ASIZE(Name))) + uiMsg(UIERROR_INVALIDNAME,UINULL,Name); + + if (CmpName(FindMask,Name,MATCH_NAMES)) + { + wchar FullName[NM]; + wcsncpyz(FullName,FindMask,ASIZE(FullName)); + *PointToName(FullName)=0; + if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) + { + uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); + return false; + } + wcsncatz(FullName,Name,ASIZE(FullName)); + if (!FastFind(FullName,fd,GetSymLink)) + { + ErrHandler.OpenErrorMsg(FullName); + continue; + } + wcsncpyz(fd->Name,FullName,ASIZE(fd->Name)); + break; + } + } +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + + FirstCall=false; + wchar *NameOnly=PointToName(fd->Name); + if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) + return Next(fd); + return true; +} + + +bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) +{ + return true; // OPENMPT ADDITION + fd->Error=false; +#ifndef _UNIX + if (IsWildcard(FindMask)) + return false; +#endif +#ifdef _WIN_ALL + HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd); + if (hFind==INVALID_HANDLE_VALUE) + return false; + FindClose(hFind); +#else + char FindMaskA[NM]; + WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); + + struct stat st; + if (GetSymLink) + { +#ifdef SAVE_LINKS + if (lstat(FindMaskA,&st)!=0) +#else + if (stat(FindMaskA,&st)!=0) +#endif + { + fd->Error=(errno!=ENOENT); + return false; + } + } + else + if (stat(FindMaskA,&st)!=0) + { + fd->Error=(errno!=ENOENT); + return false; + } + fd->FileAttr=st.st_mode; + fd->Size=st.st_size; + +#ifdef UNIX_TIME_NS +#if defined(_APPLE) + fd->mtime.SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); + fd->atime.SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); + fd->ctime.SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); +#else + fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); + fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); + fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); +#endif +#else + fd->mtime.SetUnix(st.st_mtime); + fd->atime.SetUnix(st.st_atime); + fd->ctime.SetUnix(st.st_ctime); +#endif + + wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + + return true; +} + + +#ifdef _WIN_ALL +HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) +{ + WIN32_FIND_DATA FindData; + if (hFind==INVALID_HANDLE_VALUE) + { + hFind=FindFirstFile(Mask,&FindData); + if (hFind==INVALID_HANDLE_VALUE) + { + wchar LongMask[NM]; + if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) + hFind=FindFirstFile(LongMask,&FindData); + } + if (hFind==INVALID_HANDLE_VALUE) + { + int SysErr=GetLastError(); + // We must not issue an error for "file not found" and "path not found", + // because it is normal to not find anything for wildcard mask when + // archiving. Also searching for non-existent file is normal in some + // other modules, like WinRAR scanning for winrar_theme_description.txt + // to check if any themes are available. + fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && + SysErr!=ERROR_PATH_NOT_FOUND && + SysErr!=ERROR_NO_MORE_FILES; + } + } + else + if (!FindNextFile(hFind,&FindData)) + { + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; + } + + if (hFind!=INVALID_HANDLE_VALUE) + { + wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); + SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime.SetWinFT(&FindData.ftLastWriteTime); + fd->ctime.SetWinFT(&FindData.ftCreationTime); + fd->atime.SetWinFT(&FindData.ftLastAccessTime); + + + } + fd->Flags=0; + return hFind; +} +#endif + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/find.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/find.hpp new file mode 100644 index 00000000..250637f8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/find.hpp @@ -0,0 +1,49 @@ +#ifndef _RAR_FINDDATA_ +#define _RAR_FINDDATA_ + +enum FINDDATA_FLAGS { + FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. +}; + +struct FindData +{ + wchar Name[NM]; + uint64 Size; + uint FileAttr; + bool IsDir; + bool IsLink; + RarTime mtime; + RarTime ctime; + RarTime atime; +#ifdef _WIN_ALL + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; +#endif + uint Flags; + bool Error; +}; + +class FindFile +{ + private: +#ifdef _WIN_ALL + static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); +#endif + + wchar FindMask[NM]; + bool FirstCall; +#ifdef _WIN_ALL + HANDLE hFind; +#else + DIR *dirp; +#endif + public: + FindFile(); + ~FindFile(); + void SetMask(const wchar *Mask); + bool Next(FindData *fd,bool GetSymLink=false); + static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.cpp new file mode 100644 index 00000000..e4db2695 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.cpp @@ -0,0 +1,52 @@ +#include "rar.hpp" + +BitInput::BitInput(bool AllocBuffer) +{ + ExternalBuffer=false; + if (AllocBuffer) + { + // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // So let's allocate 3 additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 3 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+3; + InBuf=new byte[BufSize]; + + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + memset(InBuf,0,BufSize); + } + else + InBuf=NULL; +} + + +BitInput::~BitInput() +{ + if (!ExternalBuffer) + delete[] InBuf; +} + + +void BitInput::faddbits(uint Bits) +{ + // Function wrapped version of inline addbits to save code size. + addbits(Bits); +} + + +uint BitInput::fgetbits() +{ + // Function wrapped version of inline getbits to save code size. + return getbits(); +} + + +void BitInput::SetExternalBuffer(byte *Buf) +{ + if (InBuf!=NULL && !ExternalBuffer) + delete[] InBuf; + InBuf=Buf; + ExternalBuffer=true; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.hpp new file mode 100644 index 00000000..2e151da9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/getbits.hpp @@ -0,0 +1,68 @@ +#ifndef _RAR_GETBITS_ +#define _RAR_GETBITS_ + +class BitInput +{ + public: + enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. + + int InAddr; // Curent byte position in the buffer. + int InBit; // Current bit position in the current byte. + + bool ExternalBuffer; + public: + BitInput(bool AllocBuffer); + ~BitInput(); + + byte *InBuf; // Dynamically allocated input buffer. + + void InitBitInput() + { + InAddr=InBit=0; + } + + // Move forward by 'Bits' bits. + void addbits(uint Bits) + { + Bits+=InBit; + InAddr+=Bits>>3; + InBit=Bits&7; + } + + // Return 16 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits() + { + uint BitField=(uint)InBuf[InAddr] << 16; + BitField|=(uint)InBuf[InAddr+1] << 8; + BitField|=(uint)InBuf[InAddr+2]; + BitField >>= (8-InBit); + return BitField & 0xffff; + } + + // Return 32 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits32() + { + uint BitField=(uint)InBuf[InAddr] << 24; + BitField|=(uint)InBuf[InAddr+1] << 16; + BitField|=(uint)InBuf[InAddr+2] << 8; + BitField|=(uint)InBuf[InAddr+3]; + BitField <<= InBit; + BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); + return BitField & 0xffffffff; + } + + void faddbits(uint Bits); + uint fgetbits(); + + // Check if buffer has enough space for IncPtr bytes. Returns 'true' + // if buffer will be overflown. + bool Overflow(uint IncPtr) + { + return InAddr+IncPtr>=MAX_SIZE; + } + + void SetExternalBuffer(byte *Buf); +}; +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/global.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/global.cpp new file mode 100644 index 00000000..771f0001 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/global.cpp @@ -0,0 +1,7 @@ +#define INCLUDEGLOBAL + +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#include "rar.hpp" diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/global.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/global.hpp new file mode 100644 index 00000000..35c6cf91 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/global.hpp @@ -0,0 +1,14 @@ +#ifndef _RAR_GLOBAL_ +#define _RAR_GLOBAL_ + +#ifdef INCLUDEGLOBAL + #define EXTVAR +#else + #define EXTVAR extern +#endif + +EXTVAR ErrorHandler ErrHandler; + + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/hardlinks.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/hardlinks.cpp new file mode 100644 index 00000000..40cc0aa4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/hardlinks.cpp @@ -0,0 +1,39 @@ +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + if (!FileExist(NameExisting)) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + uiMsg(UIERROR_NOLINKTARGET); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + CreatePath(NameNew,true,Cmd->DisableNames); + +#ifdef _WIN_ALL + bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#elif defined(_UNIX) + char NameExistingA[NM],NameNewA[NM]; + WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); + WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); + bool Success=link(NameExistingA,NameNewA)==0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#else + return false; +#endif +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/hash.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/hash.cpp new file mode 100644 index 00000000..a4559e05 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/hash.cpp @@ -0,0 +1,135 @@ +#include "rar.hpp" + +void HashValue::Init(HASH_TYPE Type) +{ + HashValue::Type=Type; + + // Zero length data CRC32 is 0. It is important to set it when creating + // headers with no following data like directories or symlinks. + if (Type==HASH_RAR14 || Type==HASH_CRC32) + CRC32=0; + if (Type==HASH_BLAKE2) + { + // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f + // is BLAKE2sp hash of empty data. We init the structure to this value, + // so if we create a file or service header with no following data like + // "file copy" or "symlink", we set the checksum to proper value avoiding + // additional header type or size checks when extracting. + static byte EmptyHash[32]={ + 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43, + 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25, + 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1, + 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f + }; + memcpy(Digest,EmptyHash,sizeof(Digest)); + } +} + + +bool HashValue::operator == (const HashValue &cmp) +{ + if (Type==HASH_NONE || cmp.Type==HASH_NONE) + return true; + if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 || + Type==HASH_CRC32 && cmp.Type==HASH_CRC32) + return CRC32==cmp.CRC32; + if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2) + return memcmp(Digest,cmp.Digest,sizeof(Digest))==0; + return false; +} + + +DataHash::DataHash() +{ + blake2ctx=NULL; + HashType=HASH_NONE; +#ifdef RAR_SMP + ThPool=NULL; + MaxThreads=0; +#endif +} + + +DataHash::~DataHash() +{ +#ifdef RAR_SMP + delete ThPool; +#endif + cleandata(&CurCRC32, sizeof(CurCRC32)); + if (blake2ctx!=NULL) + { + cleandata(blake2ctx, sizeof(blake2sp_state)); + delete blake2ctx; + } +} + + +void DataHash::Init(HASH_TYPE Type,uint MaxThreads) +{ + if (blake2ctx==NULL) + blake2ctx=new blake2sp_state; + HashType=Type; + if (Type==HASH_RAR14) + CurCRC32=0; + if (Type==HASH_CRC32) + CurCRC32=0xffffffff; // Initial CRC32 value. + if (Type==HASH_BLAKE2) + blake2sp_init(blake2ctx); +#ifdef RAR_SMP + DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); +#endif +} + + +void DataHash::Update(const void *Data,size_t DataSize) +{ +#ifndef SFX_MODULE + if (HashType==HASH_RAR14) + CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); +#endif + if (HashType==HASH_CRC32) + CurCRC32=CRC32(CurCRC32,Data,DataSize); + + if (HashType==HASH_BLAKE2) + { +#ifdef RAR_SMP + if (MaxThreads>1 && ThPool==NULL) + ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER); + blake2ctx->ThPool=ThPool; + blake2ctx->MaxThreads=MaxThreads; +#endif + blake2sp_update( blake2ctx, (byte *)Data, DataSize); + } +} + + +void DataHash::Result(HashValue *Result) +{ + Result->Type=HashType; + if (HashType==HASH_RAR14) + Result->CRC32=CurCRC32; + if (HashType==HASH_CRC32) + Result->CRC32=CurCRC32^0xffffffff; + if (HashType==HASH_BLAKE2) + { + // Preserve the original context, so we can continue hashing if necessary. + blake2sp_state res=*blake2ctx; + blake2sp_final(&res,Result->Digest); + } +} + + +uint DataHash::GetCRC32() +{ + return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0; +} + + +bool DataHash::Cmp(HashValue *CmpValue,byte *Key) +{ + HashValue Final; + Result(&Final); + if (Key!=NULL) + ConvertHashToMAC(&Final,Key); + return Final==*CmpValue; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/hash.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/hash.hpp new file mode 100644 index 00000000..b7d879f6 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/hash.hpp @@ -0,0 +1,52 @@ +#ifndef _RAR_DATAHASH_ +#define _RAR_DATAHASH_ + +enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; + +struct HashValue +{ + void Init(HASH_TYPE Type); + bool operator == (const HashValue &cmp); + bool operator != (const HashValue &cmp) {return !(*this==cmp);} + + HASH_TYPE Type; + union + { + uint CRC32; + byte Digest[SHA256_DIGEST_SIZE]; + }; +}; + + +#ifdef RAR_SMP +class ThreadPool; +class DataHash; +#endif + + +class DataHash +{ + private: + HASH_TYPE HashType; + uint CurCRC32; + blake2sp_state *blake2ctx; + +#ifdef RAR_SMP + ThreadPool *ThPool; + + uint MaxThreads; + // Upper limit for maximum threads to prevent wasting threads in pool. + static const uint MaxHashThreads=8; +#endif + public: + DataHash(); + ~DataHash(); + void Init(HASH_TYPE Type,uint MaxThreads); + void Update(const void *Data,size_t DataSize); + void Result(HashValue *Result); + uint GetCRC32(); + bool Cmp(HashValue *CmpValue,byte *Key); + HASH_TYPE Type() {return HashType;} +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/headers.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/headers.cpp new file mode 100644 index 00000000..b042dc39 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/headers.cpp @@ -0,0 +1,61 @@ +#include "rar.hpp" + +void FileHeader::Reset(size_t SubDataSize) +{ + SubData.Alloc(SubDataSize); + BaseBlock::Reset(); + FileHash.Init(HASH_NONE); + mtime.Reset(); + atime.Reset(); + ctime.Reset(); + SplitBefore=false; + SplitAfter=false; + + UnknownUnpSize=0; + + SubFlags=0; // Important for RAR 3.0 subhead. + + CryptMethod=CRYPT_NONE; + Encrypted=false; + SaltSet=false; + UsePswCheck=false; + UseHashKey=false; + Lg2Count=0; + + Solid=false; + Dir=false; + WinSize=0; + Inherited=false; + SubBlock=false; + CommentInHeader=false; + Version=false; + LargeFile=false; + + RedirType=FSREDIR_NONE; + DirTarget=false; + UnixOwnerSet=false; +} + + +FileHeader& FileHeader::operator = (FileHeader &hd) +{ + SubData.Reset(); + memcpy(this,&hd,sizeof(*this)); + SubData.CleanData(); + SubData=hd.SubData; + return *this; +} + + +void MainHeader::Reset() +{ + HighPosAV=0; + PosAV=0; + CommentInHeader=false; + PackComment=false; + Locator=false; + QOpenOffset=0; + QOpenMaxSize=0; + RROffset=0; + RRMaxSize=0; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/headers.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/headers.hpp new file mode 100644 index 00000000..6af453a9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/headers.hpp @@ -0,0 +1,354 @@ +#ifndef _RAR_HEADERS_ +#define _RAR_HEADERS_ + +#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header. +#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header. +#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. +#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. +#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. +#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_LONGBLOCKHEAD 11 +#define SIZEOF_SUBBLOCKHEAD 14 +#define SIZEOF_COMMHEAD 13 +#define SIZEOF_PROTECTHEAD 26 +#define SIZEOF_UOHEAD 18 +#define SIZEOF_STREAMHEAD 26 + +#define VER_PACK 29U +#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive. +#define VER_UNPACK 29U +#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive. +#define VER_UNKNOWN 9999U // Just some large value. + +#define MHD_VOLUME 0x0001U + +// Old style main archive comment embed into main archive header. Must not +// be used in new archives anymore. +#define MHD_COMMENT 0x0002U + +#define MHD_LOCK 0x0004U +#define MHD_SOLID 0x0008U +#define MHD_PACK_COMMENT 0x0010U +#define MHD_NEWNUMBERING 0x0010U +#define MHD_AV 0x0020U +#define MHD_PROTECT 0x0040U +#define MHD_PASSWORD 0x0080U +#define MHD_FIRSTVOLUME 0x0100U + +#define LHD_SPLIT_BEFORE 0x0001U +#define LHD_SPLIT_AFTER 0x0002U +#define LHD_PASSWORD 0x0004U + +// Old style file comment embed into file header. Must not be used +// in new archives anymore. +#define LHD_COMMENT 0x0008U + +// For non-file subheaders it denotes 'subblock having a parent file' flag. +#define LHD_SOLID 0x0010U + + +#define LHD_WINDOWMASK 0x00e0U +#define LHD_WINDOW64 0x0000U +#define LHD_WINDOW128 0x0020U +#define LHD_WINDOW256 0x0040U +#define LHD_WINDOW512 0x0060U +#define LHD_WINDOW1024 0x0080U +#define LHD_WINDOW2048 0x00a0U +#define LHD_WINDOW4096 0x00c0U +#define LHD_DIRECTORY 0x00e0U + +#define LHD_LARGE 0x0100U +#define LHD_UNICODE 0x0200U +#define LHD_SALT 0x0400U +#define LHD_VERSION 0x0800U +#define LHD_EXTTIME 0x1000U + +#define SKIP_IF_UNKNOWN 0x4000U +#define LONG_BLOCK 0x8000U + +#define EARC_NEXT_VOLUME 0x0001U // Not last volume. +#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). +#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. +#define EARC_VOLNUMBER 0x0008U // Store a number of current volume. + +enum HEADER_TYPE { + // RAR 5.0 header types. + HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03, + HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff, + + // RAR 1.5 - 4.x header types. + HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75, + HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79, + HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b +}; + + +// RAR 2.9 and earlier. +enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, + NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; + + +// Internal implementation, depends on archive format version. +enum HOST_SYSTEM { + // RAR 5.0 host OS + HOST5_WINDOWS=0,HOST5_UNIX=1, + + // RAR 3.0 host OS. + HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, + HOST_BEOS=5,HOST_MAX +}; + +// Unified archive format independent implementation. +enum HOST_SYSTEM_TYPE { + HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN +}; + + +// We also use these values in extra field, so do not modify them. +enum FILE_SYSTEM_REDIRECT { + FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, + FSREDIR_HARDLINK, FSREDIR_FILECOPY +}; + + +#define SUBHEAD_TYPE_CMT L"CMT" +#define SUBHEAD_TYPE_QOPEN L"QO" +#define SUBHEAD_TYPE_ACL L"ACL" +#define SUBHEAD_TYPE_STREAM L"STM" +#define SUBHEAD_TYPE_UOWNER L"UOW" +#define SUBHEAD_TYPE_AV L"AV" +#define SUBHEAD_TYPE_RR L"RR" +#define SUBHEAD_TYPE_OS2EA L"EA2" + +/* new file inherits a subblock when updating a host file */ +#define SUBHEAD_FLAGS_INHERITED 0x80000000 + +#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 + + +struct MarkHeader +{ + byte Mark[8]; + + // Following fields are virtual and not present in real blocks. + uint HeadSize; +}; + + +struct BaseBlock +{ + uint HeadCRC; // 'ushort' for RAR 1.5. + HEADER_TYPE HeaderType; // 1 byte for RAR 1.5. + uint Flags; // 'ushort' for RAR 1.5. + uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0. + + bool SkipIfUnknown; + + void Reset() + { + SkipIfUnknown=false; + } +}; + + +struct BlockHeader:BaseBlock +{ + uint DataSize; +}; + + +struct MainHeader:BaseBlock +{ + ushort HighPosAV; + uint PosAV; + bool CommentInHeader; + bool PackComment; // For RAR 1.4 archive format only. + bool Locator; + uint64 QOpenOffset; // Offset of quick list record. + uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. + uint64 RROffset; // Offset of recovery record. + uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + void Reset(); +}; + + +struct FileHeader:BlockHeader +{ + byte HostOS; + uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5. + byte Method; + union { + uint FileAttr; + uint SubFlags; + }; + wchar FileName[NM]; + + Array<byte> SubData; + + RarTime mtime; + RarTime ctime; + RarTime atime; + + int64 PackSize; + int64 UnpSize; + int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size. + + HashValue FileHash; + + uint FileFlags; + + bool SplitBefore; + bool SplitAfter; + + bool UnknownUnpSize; + + bool Encrypted; + CRYPT_METHOD CryptMethod; + bool SaltSet; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + bool UsePswCheck; + byte PswCheck[SIZE_PSWCHECK]; + + // Use HMAC calculated from HashKey and checksum instead of plain checksum. + bool UseHashKey; + + // Key to convert checksum to HMAC. Derived from password with PBKDF2 + // using additional iterations. + byte HashKey[SHA256_DIGEST_SIZE]; + + uint Lg2Count; // Log2 of PBKDF2 repetition count. + + bool Solid; + bool Dir; + bool CommentInHeader; // RAR 2.0 file comment. + bool Version; // name.ext;ver file name containing the version number. + size_t WinSize; + bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). + + // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. + bool LargeFile; + + // 'true' for HEAD_SERVICE block, which is a child of preceding file block. + // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. + bool SubBlock; + + HOST_SYSTEM_TYPE HSType; + + FILE_SYSTEM_REDIRECT RedirType; + wchar RedirName[NM]; + bool DirTarget; + + bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; + char UnixOwnerName[256],UnixGroupName[256]; +#ifdef _UNIX + uid_t UnixOwnerID; + gid_t UnixGroupID; +#else // Need these Unix fields in Windows too for 'list' command. + uint UnixOwnerID; + uint UnixGroupID; +#endif + + void Reset(size_t SubDataSize=0); + + bool CmpName(const wchar *Name) + { + return(wcscmp(FileName,Name)==0); + } + + FileHeader& operator = (FileHeader &hd); +}; + + +struct EndArcHeader:BaseBlock +{ + // Optional CRC32 of entire archive up to start of EndArcHeader block. + // Present in RAR 4.x archives if EARC_DATACRC flag is set. + uint ArcDataCRC; + + uint VolNumber; // Optional number of current volume. + + // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. + + bool NextVolume; // Not last volume. + bool DataCRC; + bool RevSpace; + bool StoreVolNumber; + void Reset() + { + BaseBlock::Reset(); + NextVolume=false; + DataCRC=false; + RevSpace=false; + StoreVolNumber=false; + } +}; + + +struct CryptHeader:BaseBlock +{ + bool UsePswCheck; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte Salt[SIZE_SALT50]; + byte PswCheck[SIZE_PSWCHECK]; +}; + + +// SubBlockHeader and its successors were used in RAR 2.x format. +// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks. +struct SubBlockHeader:BlockHeader +{ + ushort SubType; + byte Level; +}; + + +struct CommentHeader:BaseBlock +{ + ushort UnpSize; + byte UnpVer; + byte Method; + ushort CommCRC; +}; + + +struct ProtectHeader:BlockHeader +{ + byte Version; + ushort RecSectors; + uint TotalBlocks; + byte Mark[8]; +}; + + +struct UnixOwnersHeader:SubBlockHeader +{ + ushort OwnerNameSize; + ushort GroupNameSize; +/* dummy */ + char OwnerName[256]; + char GroupName[256]; +}; + + +struct EAHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint EACRC; +}; + + +struct StreamHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint StreamCRC; + ushort StreamNameSize; + char StreamName[260]; +}; + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/headers5.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/headers5.hpp new file mode 100644 index 00000000..9ea8d979 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/headers5.hpp @@ -0,0 +1,100 @@ +#ifndef _RAR_HEADERS5_ +#define _RAR_HEADERS5_ + +#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length. +#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size. + +// RAR 5.0 block flags common for all blocks. + +// Additional extra area is present in the end of block header. +#define HFL_EXTRA 0x0001 +// Additional data area is present in the end of block header. +#define HFL_DATA 0x0002 +// Unknown blocks with this flag must be skipped when updating an archive. +#define HFL_SKIPIFUNKNOWN 0x0004 +// Data area of this block is continuing from previous volume. +#define HFL_SPLITBEFORE 0x0008 +// Data area of this block is continuing in next volume. +#define HFL_SPLITAFTER 0x0010 +// Block depends on preceding file block. +#define HFL_CHILD 0x0020 +// Preserve a child block if host is modified. +#define HFL_INHERITED 0x0040 + +// RAR 5.0 main archive header specific flags. +#define MHFL_VOLUME 0x0001 // Volume. +#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first. +#define MHFL_SOLID 0x0004 // Solid archive. +#define MHFL_PROTECT 0x0008 // Recovery record is present. +#define MHFL_LOCK 0x0010 // Locked archive. + +// RAR 5.0 file header specific flags. +#define FHFL_DIRECTORY 0x0001 // Directory. +#define FHFL_UTIME 0x0002 // Time field in Unix format is present. +#define FHFL_CRC32 0x0004 // CRC32 field is present. +#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size. + +// RAR 5.0 end of archive header specific flags. +#define EHFL_NEXTVOLUME 0x0001 // Not last volume. + +// RAR 5.0 archive encryption header specific flags. +#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present. + + +// RAR 5.0 file compression flags. +#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. +#define FCI_ALGO_BIT1 0x0002 // 0 .. 63. +#define FCI_ALGO_BIT2 0x0004 +#define FCI_ALGO_BIT3 0x0008 +#define FCI_ALGO_BIT4 0x0010 +#define FCI_ALGO_BIT5 0x0020 +#define FCI_SOLID 0x0040 // Solid flag. +#define FCI_METHOD_BIT0 0x0080 // Compression method. +#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). +#define FCI_METHOD_BIT2 0x0200 +#define FCI_DICT_BIT0 0x0400 // Dictionary size. +#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT2 0x1000 +#define FCI_DICT_BIT3 0x2000 + +// Main header extra field values. +#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. + +// Flags for MHEXTRA_LOCATOR. +#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. +#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. + +// File and service header extra field values. +#define FHEXTRA_CRYPT 0x01 // Encryption parameters. +#define FHEXTRA_HASH 0x02 // File hash. +#define FHEXTRA_HTIME 0x03 // High precision file time. +#define FHEXTRA_VERSION 0x04 // File version information. +#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.). +#define FHEXTRA_UOWNER 0x06 // Unix owner and group information. +#define FHEXTRA_SUBDATA 0x07 // Service header subdata array. + + +// Hash type values for FHEXTRA_HASH. +#define FHEXTRA_HASH_BLAKE2 0x00 + +// Flags for FHEXTRA_HTIME. +#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format. +#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present. +#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present. +#define FHEXTRA_HTIME_ATIME 0x08 // atime is present. +#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision. + +// Flags for FHEXTRA_CRYPT. +#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data. +#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums. + +// Flags for FHEXTRA_REDIR. +#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory. + +// Flags for FHEXTRA_UOWNER. +#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present. +#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present. +#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present. +#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present. + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.cpp new file mode 100644 index 00000000..f85472c3 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.cpp @@ -0,0 +1,114 @@ +#include "rar.hpp" + +DWORD WinNT() +{ + static int dwPlatformId=-1; + static DWORD dwMajorVersion,dwMinorVersion; + if (dwPlatformId==-1) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + dwPlatformId=WinVer.dwPlatformId; + dwMajorVersion=WinVer.dwMajorVersion; + dwMinorVersion=WinVer.dwMinorVersion; + + } + DWORD Result=0; + if (dwPlatformId==VER_PLATFORM_WIN32_NT) + Result=dwMajorVersion*0x100+dwMinorVersion; + + + return Result; +} + + +// Replace it with documented Windows 11 check when available. +#include <comdef.h> +#include <Wbemidl.h> +#pragma comment(lib, "wbemuuid.lib") + +static bool WMI_IsWindows10() +{ + IWbemLocator *pLoc = NULL; + + HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER, + IID_IWbemLocator,(LPVOID *)&pLoc); + + if (FAILED(hres)) + return false; + + IWbemServices *pSvc = NULL; + + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc); + + if (FAILED(hres)) + { + pLoc->Release(); + return false; + } + + hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL, + RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IEnumWbemClassObject *pEnumerator = NULL; + hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + bool Win10=false; + while (pEnumerator!=NULL) + { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (uReturn==0) + break; + + VARIANT vtProp; + + hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); + Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL; + VariantClear(&vtProp); + + pclsObj->Release(); + } + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + + return Win10; +} + + +// Replace it with actual check when available. +bool IsWindows11OrGreater() +{ + static bool IsSet=false,IsWin11=false; + if (!IsSet) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + IsWin11=WinVer.dwMajorVersion>10 || + WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); + IsSet=true; + } + return IsWin11; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.hpp new file mode 100644 index 00000000..fed0b517 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/isnt.hpp @@ -0,0 +1,16 @@ +#ifndef _RAR_ISNT_ +#define _RAR_ISNT_ + +enum WINNT_VERSION { + WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, + WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601, + WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00 +}; + +DWORD WinNT(); + + +// Replace it with actual check when available. +bool IsWindows11OrGreater(); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/license.txt b/Src/external_dependencies/openmpt-trunk/include/unrar/license.txt new file mode 100644 index 00000000..a4ef8bab --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/license.txt @@ -0,0 +1,42 @@ + ****** ***** ****** UnRAR - free utility for RAR archives
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ****** ******* ****** License for use and distribution of
+ ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ** ** ** ** ** ** FREE portable version
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ The source code of UnRAR utility is freeware. This means:
+
+ 1. All copyrights to RAR and the utility UnRAR are exclusively
+ owned by the author - Alexander Roshal.
+
+ 2. UnRAR source code may be used in any software to handle
+ RAR archives without limitations free of charge, but cannot be
+ used to develop RAR (WinRAR) compatible archiver and to
+ re-create RAR compression algorithm, which is proprietary.
+ Distribution of modified UnRAR source code in separate form
+ or as a part of other software is permitted, provided that
+ full text of this paragraph, starting from "UnRAR source code"
+ words, is included in license, or in documentation if license
+ is not available, and in source code comments of resulting package.
+
+ 3. The UnRAR utility may be freely distributed. It is allowed
+ to distribute UnRAR inside of other software packages.
+
+ 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS".
+ NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
+ YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
+ DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
+ OR MISUSING THIS SOFTWARE.
+
+ 5. Installing and using the UnRAR utility signifies acceptance of
+ these terms and conditions of the license.
+
+ 6. If you don't agree with terms of the license you must remove
+ UnRAR files from your storage devices and cease to use the
+ utility.
+
+ Thank you for your interest in RAR and UnRAR.
+
+
+ Alexander L. Roshal
diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/list.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/list.cpp new file mode 100644 index 00000000..e5052c01 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/list.cpp @@ -0,0 +1,482 @@ +#include "rar.hpp" + +static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames); +static void ListSymLink(Archive &Arc); +static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); +static void ListOldSubHeader(Archive &Arc); +static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); + +void ListArchive(CommandData *Cmd) +{ + int64 SumPackSize=0,SumUnpSize=0; + uint ArcCount=0,SumFileCount=0; + bool Technical=(Cmd->Command[1]=='T'); + bool ShowService=Technical && Cmd->Command[2]=='A'; + bool Bare=(Cmd->Command[1]=='B'); + bool Verbose=(Cmd->Command[0]=='V'); + + wchar ArcName[NM]; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + + Archive Arc(Cmd); + if (!Arc.WOpen(ArcName)) + continue; + bool FileMatched=true; + while (true) + { + int64 TotalPackSize=0,TotalUnpSize=0; + uint FileCount=0; + if (Arc.IsArchive(true)) + { + bool TitleShown=false; + if (!Bare) + { + Arc.ViewComment(); + mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: ",St(MListDetails)); + uint SetCount=0; + const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); + if (Arc.Solid) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); + if (Arc.SFXSize>0) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); + if (Arc.Volume) + if (Arc.Format==RARFMT50) + { + // RAR 5.0 archives store the volume number in main header, + // so it is already available now. + if (SetCount++ > 0) + mprintf(L", "); + mprintf(St(MVolumeNumber),Arc.VolNumber+1); + } + else + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); + if (Arc.Protected) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); + if (Arc.Locked) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); + if (Arc.Encrypted) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + mprintf(L"\n"); + } + + wchar VolNumText[50]; + *VolNumText=0; + while (Arc.ReadHeader()>0) + { + Wait(); // Allow quit listing with Ctrl+C. + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { +#ifndef SFX_MODULE + // Only RAR 1.5 archives store the volume number in end record. + if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15) + swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1); +#endif + if (Technical && ShowService) + { + mprintf(L"\n%12ls: %ls",St(MListService),L"EOF"); + if (*VolNumText!=0) + mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText); + mprintf(L"\n"); + } + break; + } + switch(HeaderType) + { + case HEAD_FILE: + FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0; + if (FileMatched) + { + ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames); + if (!Arc.FileHead.SplitBefore) + { + TotalUnpSize+=Arc.FileHead.UnpSize; + FileCount++; + } + TotalPackSize+=Arc.FileHead.PackSize; + } + break; + case HEAD_SERVICE: + if (FileMatched && !Bare) + { + if (Technical && ShowService) + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,Cmd->DisableNames); + } + break; + } + Arc.SeekToNext(); + } + if (!Bare && !Technical) + if (TitleShown) + { + wchar UnpSizeText[20]; + itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + + wchar PackSizeText[20]; + itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + { + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, + PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), + VolNumText,FileCount); + } + else + { + mprintf(L"\n----------- --------- ---------- ----- ----"); + mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); + } + + SumFileCount+=FileCount; + SumUnpSize+=TotalUnpSize; + SumPackSize+=TotalPackSize; + mprintf(L"\n"); + } + else + mprintf(St(MListNoFiles)); + + ArcCount++; + +#ifndef NOVOLUME + if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || + Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && + MergeArchive(Arc,NULL,false,Cmd->Command[0])) + Arc.Seek(0,SEEK_SET); + else +#endif + break; + } + else + { + if (Cmd->ArcNames.ItemsCount()<2 && !Bare) + mprintf(St(MNotRAR),Arc.FileName); + break; + } + } + } + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (ArcCount>1 && !Bare && !Technical) + { + wchar UnpSizeText[20],PackSizeText[20]; + itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText, + ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount); + else + mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount); + } +} + + +enum LISTCOL_TYPE { + LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR +}; + + +void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames) +{ + if (!TitleShown && !Technical && !Bare) + { + if (Verbose) + { + mprintf(L"\n%ls",St(MListTitleV)); + if (!DisableNames) + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + } + else + { + mprintf(L"\n%ls",St(MListTitleL)); + if (!DisableNames) + mprintf(L"\n----------- --------- ---------- ----- ----"); + } + // Must be set even in DisableNames mode to suppress "0 files" output + // unless no files are matched. + TitleShown=true; + } + if (DisableNames) + return; + + wchar *Name=hd.FileName; + RARFORMAT Format=Arc.Format; + + if (Bare) + { + mprintf(L"%s\n",Name); + return; + } + + wchar UnpSizeText[30],PackSizeText[30]; + if (hd.UnpSize==INT64NDF) + wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText)); + else + itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText)); + + wchar AttrStr[30]; + if (hd.HeaderType==HEAD_SERVICE) + swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.'); + else + ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr)); + + wchar RatioStr[10]; + + if (hd.SplitBefore && hd.SplitAfter) + wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr)); + else + if (hd.SplitBefore) + wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr)); + else + if (hd.SplitAfter) + wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr)); + else + swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); + + wchar DateStr[50]; + hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); + + if (Technical) + { + mprintf(L"\n%12s: %s",St(MListName),Name); + + bool FileBlock=hd.HeaderType==HEAD_FILE; + + if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + { + mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); + } + else + { + const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService); + + if (hd.RedirType!=FSREDIR_NONE) + switch(hd.RedirType) + { + case FSREDIR_UNIXSYMLINK: + Type=St(MListUSymlink); break; + case FSREDIR_WINSYMLINK: + Type=St(MListWSymlink); break; + case FSREDIR_JUNCTION: + Type=St(MListJunction); break; + case FSREDIR_HARDLINK: + Type=St(MListHardlink); break; + case FSREDIR_FILECOPY: + Type=St(MListCopy); break; + } + mprintf(L"\n%12ls: %ls",St(MListType),Type); + if (hd.RedirType!=FSREDIR_NONE) + if (Format==RARFMT15) + { + char LinkTargetA[NM]; + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); + } + else + { + int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); + Arc.Read(LinkTargetA,DataSize); + LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; + } + wchar LinkTarget[NM]; + CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); + mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); + } + else + mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); + } + if (!hd.Dir) + { + mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); + mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); + mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); + } + bool WinTitles=false; +#ifdef _WIN_ALL + WinTitles=true; +#endif + if (hd.mtime.IsSet()) + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr); + if (hd.ctime.IsSet()) + { + hd.ctime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr); + } + if (hd.atime.IsSet()) + { + hd.atime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr); + } + mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"\n%12ls: %8.8X", + hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32", + hd.FileHash.CRC32); + if (hd.FileHash.Type==HASH_BLAKE2) + { + wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; + BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); + mprintf(L"\n%12ls: %ls", + hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", + BlakeStr); + } + + const wchar *HostOS=L""; + if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN) + HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix"; + if (Format==RARFMT15) + { + static const wchar *RarOS[]={ + L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L"" + }; + if (hd.HostOS<ASIZE(RarOS)) + HostOS=RarOS[hd.HostOS]; + } + if (*HostOS!=0) + mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS); + + mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo), + Format==RARFMT15 ? L"1.5":L"5.0", + hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method, + hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, + hd.WinSize>=0x100000 ? L"M":L"K"); + + if (hd.Solid || hd.Encrypted) + { + mprintf(L"\n%12ls: ",St(MListFlags)); + if (hd.Solid) + mprintf(L"%ls ",St(MListSolid)); + if (hd.Encrypted) + mprintf(L"%ls ",St(MListEnc)); + } + + if (hd.Version) + { + uint Version=ParseVersionFileName(Name,false); + if (Version!=0) + mprintf(L"\n%12ls: %u",St(MListFileVer),Version); + } + + if (hd.UnixOwnerSet) + { + mprintf(L"\n%12ls: ",L"Unix owner"); + if (*hd.UnixOwnerName!=0) + mprintf(L"%ls",GetWide(hd.UnixOwnerName)); + else + if (hd.UnixOwnerNumeric) + mprintf(L"#%d",hd.UnixOwnerID); + mprintf(L":"); + if (*hd.UnixGroupName!=0) + mprintf(L"%ls",GetWide(hd.UnixGroupName)); + else + if (hd.UnixGroupNumeric) + mprintf(L"#%d",hd.UnixGroupID); + } + + mprintf(L"\n"); + return; + } + + mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); + + if (Verbose) + mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); + + mprintf(L" %ls ",DateStr); + + if (Verbose) + { + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"%8.8X ",hd.FileHash.CRC32); + else + if (hd.FileHash.Type==HASH_BLAKE2) + { + byte *S=hd.FileHash.Digest; + mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); + } + else + mprintf(L"???????? "); + } + mprintf(L"%ls",Name); +} + +/* +void ListSymLink(Archive &Arc) +{ + if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); + } + else + { + char FileName[NM]; + uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); + Arc.Read(FileName,DataSize); + FileName[DataSize]=0; + mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); + } +} +*/ + +void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) +{ + switch(HostType) + { + case HSYS_WINDOWS: + swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c", + (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed. + (A & 0x0800)!=0 ? 'C' : '.', // Compressed. + (A & 0x0020)!=0 ? 'A' : '.', // Archive. + (A & 0x0010)!=0 ? 'D' : '.', // Directory. + (A & 0x0004)!=0 ? 'S' : '.', // System. + (A & 0x0002)!=0 ? 'H' : '.', // Hidden. + (A & 0x0001)!=0 ? 'R' : '.'); // Read-only. + break; + case HSYS_UNIX: + switch (A & 0xF000) + { + case 0x4000: + AttrStr[0]='d'; + break; + case 0xA000: + AttrStr[0]='l'; + break; + default: + AttrStr[0]='-'; + break; + } + swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c", + (A & 0x0100) ? 'r' : '-', + (A & 0x0080) ? 'w' : '-', + (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'), + (A & 0x0020) ? 'r' : '-', + (A & 0x0010) ? 'w' : '-', + (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'), + (A & 0x0004) ? 'r' : '-', + (A & 0x0002) ? 'w' : '-', + (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-'); + break; + case HSYS_UNKNOWN: + wcsncpyz(AttrStr,L"?",AttrSize); + break; + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/list.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/list.hpp new file mode 100644 index 00000000..7721ae52 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/list.hpp @@ -0,0 +1,6 @@ +#ifndef _RAR_LIST_ +#define _RAR_LIST_ + +void ListArchive(CommandData *Cmd); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/loclang.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/loclang.hpp new file mode 100644 index 00000000..ad7d0c92 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/loclang.hpp @@ -0,0 +1,396 @@ +#define MYesNo L"_Yes_No" +#define MYesNoAll L"_Yes_No_All" +#define MYesNoAllQ L"_Yes_No_All_nEver_Quit" +#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit" +#define MContinueQuit L"_Continue_Quit" +#define MRetryAbort L"_Retry_Abort" +#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit" +#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" +#define MRegTo L"\nRegistered to %s\n" +#define MShare L"\nTrial version Type 'rar -?' for help\n" +#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n" +#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n" +#define MBeta L"beta" +#define Mx86 L"x86" +#define Mx64 L"x64" +#define MMonthJan L"Jan" +#define MMonthFeb L"Feb" +#define MMonthMar L"Mar" +#define MMonthApr L"Apr" +#define MMonthMay L"May" +#define MMonthJun L"Jun" +#define MMonthJul L"Jul" +#define MMonthAug L"Aug" +#define MMonthSep L"Sep" +#define MMonthOct L"Oct" +#define MMonthNov L"Nov" +#define MMonthDec L"Dec" +#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>" +#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>" +#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>" +#define MCHelpCmd L"\n\n<Commands>" +#define MCHelpCmdA L"\n a Add files to archive" +#define MCHelpCmdC L"\n c Add archive comment" +#define MCHelpCmdCH L"\n ch Change archive parameters" +#define MCHelpCmdCW L"\n cw Write archive comment to file" +#define MCHelpCmdD L"\n d Delete files from archive" +#define MCHelpCmdE L"\n e Extract files without archived paths" +#define MCHelpCmdF L"\n f Freshen files in archive" +#define MCHelpCmdI L"\n i[par]=<str> Find string in archives" +#define MCHelpCmdK L"\n k Lock archive" +#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]" +#define MCHelpCmdM L"\n m[f] Move to archive [files only]" +#define MCHelpCmdP L"\n p Print file to stdout" +#define MCHelpCmdR L"\n r Repair archive" +#define MCHelpCmdRC L"\n rc Reconstruct missing volumes" +#define MCHelpCmdRN L"\n rn Rename archived files" +#define MCHelpCmdRR L"\n rr[N] Add data recovery record" +#define MCHelpCmdRV L"\n rv[N] Create recovery volumes" +#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX" +#define MCHelpCmdT L"\n t Test archive files" +#define MCHelpCmdU L"\n u Update files in archive" +#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]" +#define MCHelpCmdX L"\n x Extract files with full path" +#define MCHelpSw L"\n\n<Switches>" +#define MCHelpSwm L"\n - Stop switches scanning" +#define MCHelpSwAT L"\n @[+] Disable [enable] file lists" +#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction" +#define MCHelpSwAD L"\n ad[1,2] Alternate destination path" +#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" +#define MCHelpSwAI L"\n ai Ignore file attributes" +#define MCHelpSwAO L"\n ao Add files with Archive attribute set" +#define MCHelpSwAP L"\n ap<path> Set path inside archive" +#define MCHelpSwAS L"\n as Synchronize archive contents" +#define MCHelpSwCm L"\n c- Disable comments show" +#define MCHelpSwCFGm L"\n cfg- Disable read configuration" +#define MCHelpSwCL L"\n cl Convert names to lower case" +#define MCHelpSwCU L"\n cu Convert names to upper case" +#define MCHelpSwDF L"\n df Delete files after archiving" +#define MCHelpSwDH L"\n dh Open shared files" +#define MCHelpSwDR L"\n dr Delete files to Recycle Bin" +#define MCHelpSwDS L"\n ds Disable name sort for solid archive" +#define MCHelpSwDW L"\n dw Wipe files after archiving" +#define MCHelpSwEa L"\n e[+]<attr> Set file exclude and include attributes" +#define MCHelpSwED L"\n ed Do not add empty directories" +#define MCHelpSwEP L"\n ep Exclude paths from names" +#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names" +#define MCHelpSwEP2 L"\n ep2 Expand paths to full" +#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter" +#define MCHelpSwEP4 L"\n ep4<path> Exclude the path prefix from names" +#define MCHelpSwF L"\n f Freshen files" +#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" +#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" +#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages" +#define MCHelpSwIEML L"\n ieml[addr] Send archive by email" +#define MCHelpSwIERR L"\n ierr Send all messages to stderr" +#define MCHelpSwILOG L"\n ilog[name] Log errors to file" +#define MCHelpSwINUL L"\n inul Disable all messages" +#define MCHelpSwIOFF L"\n ioff[n] Turn PC off after completing an operation" +#define MCHelpSwISND L"\n isnd[-] Control notification sounds" +#define MCHelpSwIVER L"\n iver Display the version number" +#define MCHelpSwK L"\n k Lock archive" +#define MCHelpSwKB L"\n kb Keep broken extracted files" +#define MCHelpSwLog L"\n log[f][=name] Write names to log file" +#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)" +#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" +#define MCHelpSwMC L"\n mc<par> Set advanced compression parameters" +#define MCHelpSwMD L"\n md<n>[k,m,g] Dictionary size in KB, MB or GB" +#define MCHelpSwME L"\n me[par] Set encryption parameters" +#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store" +#define MCHelpSwMT L"\n mt<threads> Set the number of threads" +#define MCHelpSwN L"\n n<file> Additionally filter included files" +#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin" +#define MCHelpSwNal L"\n n@<list> 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 MCHelpSwOP L"\n op<path> Set the output path for extracted files" +#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 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<P>[:<S>] 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[<N>,v[-],e] Create solid archive" +#define MCHelpSwSm L"\n s- Disable solid archiving" +#define MCHelpSwSC L"\n sc<chr>[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<size> Process files with size less than specified" +#define MCHelpSwSM L"\n sm<size> 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[mcao]<t> Process files newer than <t> time" +#define MCHelpSwTO L"\n to[mcao]<t> Process files older than <t> time" +#define MCHelpSwTA L"\n ta[mcao]<d> Process files modified after <d> YYYYMMDDHHMMSS date" +#define MCHelpSwTB L"\n tb[mcao]<d> Process files modified before <d> YYYYMMDDHHMMSS date" +#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)" +#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<size>[k,b] Create volumes with size=<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<path> Assign work directory" +#define MCHelpSwX L"\n x<file> Exclude specified file" +#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin" +#define MCHelpSwXal L"\n x@<list> 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 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 MListModified L"Modified" +#define MListCreated L"Created" +#define MListAccessed L"Accessed" +#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 MRRSearch L"\nSearching for recovery record" +#define MAnalyzeFileData L"\nAnalyzing file data" +#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 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 or directory 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 MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive" +#define MCannotEmail L"\nCannot email the file %s" +#define MCopyrightS L"\nRAR SFX archive" +#define MSHelpCmd L"\n\n<Commands>" +#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 MRecycleFailed L"\nCannot move some files and directories to Recycle Bin" +#define MCalcCRC L"\nCalculating the checksum" +#define MTooLargeSFXArc L"\nToo 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 MWrongFilePassword L"\nIncorrect password for %s" +#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x" +#define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated" +#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 MErrLnkTarget L"\nYou need to unpack the link target first" +#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." +#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file." +#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort." +#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n" +#define MErrReadCount L"\n%u files are archived incompletely because of read errors." +#define MDirNameExists L"\nDirectory with such name already exists" +#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin" +#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated." +#define MAdjustValue L"\nAdjusting %s value to %s." +#define MOpFailed L"\nOperation failed" +#define MSkipEncArc L"\nSkipping the encrypted archive %s" diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/log.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/log.cpp new file mode 100644 index 00000000..8bbe8ee0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/log.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/log.hpp new file mode 100644 index 00000000..008ef11a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/match.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/match.cpp new file mode 100644 index 00000000..0b27d825 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/match.cpp @@ -0,0 +1,148 @@ +#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) +{ + return true; // OPENMPT ADDITION + 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 && CmpMode!=MATCH_ALLWILD && + mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0) + { + // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD, + // "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_ALLWILD) + return match(Wildcard,Name,ForceCase); + 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/Src/external_dependencies/openmpt-trunk/include/unrar/match.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/match.hpp new file mode 100644 index 00000000..1e65a3ce --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/match.hpp @@ -0,0 +1,38 @@ +#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_ALLWILD, // Paths and names are compared using wildcards. + // Unlike MATCH_SUBPATH, paths do not match subdirs + // unless a wildcard tells so. + + 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/Src/external_dependencies/openmpt-trunk/include/unrar/model.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/model.cpp new file mode 100644 index 00000000..83391c5a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/model.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** + * 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 <class T> +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(); + if (MinContext == NULL) + throw std::bad_alloc(); + MinContext->Suffix=NULL; + OrderFall=MaxOrder; + MinContext->U.SummFreq=(MinContext->NumStats=256)+1; + FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2); + if (FoundState == NULL) + throw std::bad_alloc(); + 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) +{ + 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; + + } + // We ensure that PPM order input parameter does not exceed MAX_O (64), + // so we do not really need this check and added it for extra safety. + // See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return NULL; + + *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; + + // We do not reuse PPMd coder in unstable state, so we do not really need + // this check and added it for extra safety. See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return false; + + *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) + { + pps++; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + 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 + { + pps++; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + 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/Src/external_dependencies/openmpt-trunk/include/unrar/model.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/model.hpp new file mode 100644 index 00000000..52abc89b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/options.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/options.cpp new file mode 100644 index 00000000..40323be8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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_MAX; + FileSizeLess=INT64NDF; + FileSizeMore=INT64NDF; + HashType=HASH_CRC32; +#ifdef RAR_SMP + Threads=GetNumberOfThreads(); +#endif +#ifdef USE_QOPEN + QOpenMode=QOPEN_AUTO; +#endif +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/options.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/options.hpp new file mode 100644 index 00000000..11319582 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/options.hpp @@ -0,0 +1,222 @@ +#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) +}; + +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_MAX +}; + +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 +}; + +enum APPENDARCNAME_MODE +{ + APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR, + APPENDARCNAME_OWNDIR +}; + +enum POWER_MODE { + POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP, + POWERMODE_RESTART +}; + + +// Need "forced off" state to turn off sound in GUI command line. +enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF}; + +struct FilterMode +{ + FilterState State; + int Param1; + int Param2; +}; + +#define MAX_GENERATE_MASK 128 + + +class RAROptions +{ + public: + RAROptions(); + ~RAROptions(); + void Init(); + + uint ExclFileAttr; + uint InclFileAttr; + + // We handle -ed and -e+d with special flags instead of attribute mask, + // so it works with both Windows and Unix archives. + bool ExclDir; + bool InclDir; + + bool InclAttrSet; + size_t WinSize; + wchar TempPath[NM]; + wchar SFXModule[NM]; + +#ifdef USE_QOPEN + 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]; // For -ap<path>. + wchar ExclArcPath[NM]; // For -ep4<path> switch. + SecPassword Password; + bool EncryptHeaders; + bool SkipEncrypted; + + bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. + + wchar LogName[NM]; + MESSAGE_TYPE MsgStream; + SOUND_NOTIFY_MODE Sound; + OVERWRITE_MODE Overwrite; + int Method; + HASH_TYPE HashType; + int Recovery; + int RecVolNumber; + bool DisablePercentage; + bool DisableCopyright; + bool DisableDone; + bool DisableNames; + 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<int64> NextVolSizes; + uint CurVolNum; + bool AllYes; + bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. + 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]; + wchar DefGenerateMask[MAX_GENERATE_MASK]; +#endif + bool SyncFiles; + bool ProcessEA; + bool SaveStreams; + bool SetCompressedAttr; + bool IgnoreGeneralAttr; + RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore; + bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR; + RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter; + bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR; + int64 FileSizeLess; + int64 FileSizeMore; + bool Lock; + bool Test; + bool VolumePause; + FilterMode FilterModes[MAX_FILTER_TYPES]; + wchar EmailTo[NM]; + uint VersionControl; + APPENDARCNAME_MODE AppendArcNameToPath; + POWER_MODE Shutdown; + EXTTIME_MODE xmtime; // Extended time modes (time precision to store). + EXTTIME_MODE xctime; + EXTTIME_MODE xatime; + bool PreserveAtime; + + // Read data from stdin and store in archive under a name specified here + // when archiving. Read an archive from stdin if any non-empty string + // is specified here when extracting. + wchar UseStdin[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/Src/external_dependencies/openmpt-trunk/include/unrar/os.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/os.hpp new file mode 100644 index 00000000..f40cfcaf --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/os.hpp @@ -0,0 +1,278 @@ +#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 <new> + + +#if defined(_WIN_ALL) || defined(_EMX) + +#define LITTLE_ENDIAN +#define NM 2048 + +#ifdef _WIN_ALL + + +// We got a report that just "#define STRICT" is incompatible with +// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order +// in which these statements are reached this may cause a compiler warning +// and build break for other projects incorporating this source. +// So we changed it to "#define STRICT 1". +#ifndef STRICT +#define STRICT 1 +#endif + +// 'ifndef' check here is needed for unrar.dll header to avoid macro +// re-definition warnings in third party projects. +#ifndef UNICODE +#define UNICODE +#define _UNICODE // Set _T() macro to convert from narrow to wide strings. +#endif + +#if 0 // OPENMPT ADDITION +#define WINVER _WIN32_WINNT_WINXP +#endif // OPENMPT ADDITION +#if 0 // OPENMPT ADDITION +#define _WIN32_WINNT _WIN32_WINNT_WINXP +#endif // OPENMPT ADDITION + +#if !defined(ZIPSFX) +#define RAR_SMP +#endif + +#define WIN32_LEAN_AND_MEAN + +#include <windows.h> +#include <prsht.h> +#include <shlwapi.h> +#pragma comment(lib, "Shlwapi.lib") +#include <PowrProf.h> +#pragma comment(lib, "PowrProf.lib") +#include <shellapi.h> +#include <shlobj.h> +#include <winioctl.h> +#include <wincrypt.h> +#include <wchar.h> +#include <wctype.h> + + +#endif // _WIN_ALL + +#include <sys/types.h> +#include <sys/stat.h> +#include <dos.h> + +#if !defined(_EMX) && !defined(_MSC_VER) + #include <dir.h> +#endif +#ifdef _MSC_VER + #if _MSC_VER<1500 + #define for if (0) ; else for + #endif + #include <direct.h> + #include <intrin.h> + + #if !defined(__clang__) // OPENMPT ADDITION + // Use SSE only for x86/x64, not ARM Windows. + #if defined(_M_IX86) || defined(_M_X64) + #define USE_SSE + #define SSE_ALIGNMENT 16 + #endif + #endif // OPENMPT ADDITION +#else + #include <dirent.h> +#endif // _MSC_VER + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <dos.h> +#include <io.h> +#include <time.h> +#include <signal.h> + + +#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 <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#if defined(__QNXNTO__) + #include <sys/param.h> +#endif +#if defined(RAR_SMP) && defined(__APPLE__) + #include <sys/sysctl.h> +#endif +#ifndef SFX_MODULE + #include <sys/statvfs.h> +#endif +#include <pwd.h> +#include <grp.h> +#include <wchar.h> +#include <wctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <time.h> +#include <signal.h> +#include <utime.h> +#include <locale.h> + + +#ifdef S_IFLNK +#define SAVE_LINKS +#endif + +#if defined(__linux) || defined(__FreeBSD__) +#include <sys/time.h> +#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 + +// Unlike Apple x64, utimensat shall be available in all Apple M1 systems. +#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__) + #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/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.cpp new file mode 100644 index 00000000..7081aa97 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.cpp @@ -0,0 +1,1072 @@ +#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!=0 && 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,size_t DestSize) +{ + return (wchar *)SrcPath; // OPENMPT ADDITION + 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 any amount of <d>:\ 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)); + wcsncpyz(DestPath,TmpStr,DestSize); + } + 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) +{ + if (Str==NULL) + return false; +#ifdef _WIN_ALL + // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\') + Str+=4; +#endif + return 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<MaxLength) + { + Path[Length]=CPATHDIVIDER; + Path[Length+1]=0; + } +} + + +void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize) +{ + // 'Path', 'Name' and 'Pathname' can point to same memory area. So we use + // the temporary buffer instead of constructing the name in 'Pathname'. + wchar OutName[NM]; + wcsncpyz(OutName,Path,ASIZE(OutName)); + // Do not add slash to d:, we want to allow relative paths like d:filename. + if (!IsDriveLetter(Path) || Path[2]!=0) + AddEndSlash(OutName,ASIZE(OutName)); + wcsncatz(OutName,Name,ASIZE(OutName)); + wcsncpyz(Pathname,OutName,MaxSize); +} + + +// Returns file path including the trailing path separator symbol. +void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength) +{ + if (MaxLength==0) + return; + size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName)); + wcsncpy(Path,FullName,PathLength); + Path[PathLength]=0; +} + + +// Removes name and returns file path without the trailing +// path separator symbol. +void RemoveNameFromPath(wchar *Path) +{ + wchar *Name=PointToName(Path); + if (Name>=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) + CharToWide(EnvStr,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 or to beginning +// of file name if numeric part is missing. +wchar* GetVolNumPart(const wchar *ArcName) +{ + // We do not want to increment any characters in path component. + ArcName=PointToName(ArcName); + + if (*ArcName==0) + return (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. + const wchar *Dot=wcschr(ArcName,'.'); + if (Dot!=NULL && Dot<NumPtr) + ChPtr=NumPtr; + break; + } + NumPtr--; + } + return (wchar *)ChPtr; +} + + +void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering) +{ + wchar *ChPtr; + if ((ChPtr=GetExt(ArcName))==NULL) + { + wcsncatz(ArcName,L".rar",MaxLength); + ChPtr=GetExt(ArcName); + } + else + if (ChPtr[1]==0 || wcsicomp(ChPtr,L".exe")==0 || wcsicomp(ChPtr,L".sfx")==0) + wcsncpyz(ChPtr,L".rar",MaxLength-(ChPtr-ArcName)); + + if (ChPtr==NULL || *ChPtr!='.' || ChPtr[1]==0) + { + // Normally we shall have some extension here. If we don't, it means + // the name has no extension and buffer has no free space to append one. + // Let's clear the name to prevent a new call with same name and return. + *ArcName=0; + return; + } + + if (!OldNumbering) + { + ChPtr=GetVolNumPart(ArcName); + + // We should not check for IsDigit(*ChPtr) here and should increment + // even non-digits. If we got a corrupt archive with volume flag, + // but without numeric part, we still need to modify its name somehow, + // so while (exist(name)) {NextVolumeName()} loops do not run infinitely. + while ((++(*ChPtr))=='9'+1) + { + *ChPtr='0'; + ChPtr--; + if (ChPtr<ArcName || !IsDigit(*ChPtr)) + { + // Convert .part:.rar (.part9.rar after increment) to part10.rar. + for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--) + *(EndPtr+1)=*EndPtr; + *(ChPtr+1)='1'; + break; + } + } + } + else + if (!IsDigit(ChPtr[2]) || !IsDigit(ChPtr[3])) + wcsncpyz(ChPtr+2,L"00",MaxLength-(ChPtr-ArcName)-2); // From .rar to .r00. + else + { + ChPtr+=wcslen(ChPtr)-1; // Set to last character. + while (++(*ChPtr)=='9'+1) + if (ChPtr<=ArcName || *(ChPtr-1)=='.') + { + *ChPtr='a'; // From .999 to .a00 if started from .001 or for too short names. + break; + } + else + { + *ChPtr='0'; + ChPtr--; + } + } +} + + +bool IsNameUsable(const wchar *Name) +{ +#ifndef _UNIX + if (Name[0] && Name[1] && wcschr(Name+2,':')!=NULL) + return false; + for (const wchar *s=Name;*s!=0;s++) + { + if ((uint)*s<32) + return false; + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + return false; + } +#endif + return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==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 (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; + DestName[Copied]=0; +} + + +void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength) +{ + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; + DestName[Copied]=0; +} + + +void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; + DestName[Copied]=0; +} + + +void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; + DestName[Copied]=0; +} + + +void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize) +{ + if (Src==NULL || *Src==0) + { + if (MaxSize>0) + *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<ASIZE(FullName)) + wcsncpyz(Dest,FullName,MaxSize); + else + if (Src!=Dest) + wcsncpyz(Dest,Src,MaxSize); + } +#elif defined(_UNIX) + if (IsFullPath(Src)) + *Dest=0; + else + { + char CurDirA[NM]; + if (getcwd(CurDirA,ASIZE(CurDirA))==NULL) + *CurDirA=0; + CharToWide(CurDirA,Dest,MaxSize); + AddEndSlash(Dest,MaxSize); + } + wcsncatz(Dest,Src,MaxSize); +#else + wcsncpyz(Dest,Src,MaxSize); +#endif +} + + +bool IsFullPath(const wchar *Path) +{ +/* + wchar PathOnly[NM]; + GetFilePath(Path,PathOnly,ASIZE(PathOnly)); + if (IsWildcard(PathOnly)) + return true; +*/ +#if defined(_WIN_ALL) || defined(_EMX) + return Path[0]=='\\' && Path[1]=='\\' || IsDriveLetter(Path) && IsPathDiv(Path[2]); +#else + return IsPathDiv(Path[0]); +#endif +} + + +bool IsFullRootPath(const wchar *Path) +{ + return IsFullPath(Path) || IsPathDiv(Path[0]); +} + + +void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize) +{ + *Root=0; + if (IsDriveLetter(Path)) + swprintf(Root,MaxSize,L"%c:\\",*Path); + else + if (Path[0]=='\\' && Path[1]=='\\') + { + const wchar *Slash=wcschr(Path+2,'\\'); + if (Slash!=NULL) + { + size_t Length; + if ((Slash=wcschr(Slash+1,'\\'))!=NULL) + Length=Slash-Path+1; + else + Length=wcslen(Path); + if (Length>=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) + { + 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, does 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,size_t MaxSize,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; + uint MAsMinutes=0; // By default we treat 'M' as months. + 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') + MAsMinutes=2; // Treat next two 'M' after 'H' as minutes. + if (CurChar=='D' || CurChar=='Y') + MAsMinutes=0; // Treat 'M' in HHDDMMYY and HHYYMMDD as month. + + if (MAsMinutes>0 && 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'; + MAsMinutes--; + } + 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<Digits) + { + wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1); + wmemset(Mask+I,'N',Digits); + } + I+=Max(Digits,NCount)-1; + ArcNumPresent=true; + continue; + } + } + + RarTime CurTime; + CurTime.SetCurrentTime(); + RarLocalTime rlt; + CurTime.GetLocal(&rlt); + + wchar Ext[NM],*Dot=GetExt(ArcName); + *Ext=0; + if (Dot==NULL) + wcsncpyz(Ext,*PointToName(ArcName)==0 ? L".rar":L"",ASIZE(Ext)); + else + { + wcsncpyz(Ext,Dot,ASIZE(Ext)); + *Dot=0; + } + + int WeekDay=rlt.wDay==0 ? 6:rlt.wDay-1; + int StartWeekDay=rlt.yDay-WeekDay; + if (StartWeekDay<0) + if (StartWeekDay<=-4) + StartWeekDay+=IsLeapYear(rlt.Year-1) ? 366:365; + else + StartWeekDay=0; + int CurWeek=StartWeekDay/7+1; + if (StartWeekDay%7>=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"; + + // How many times every modifier character was encountered in the mask. + int CField[sizeof(Field)/sizeof(Field[0])]; + + memset(CField,0,sizeof(CField)); + QuoteMode=false; + for (uint 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 && J<ASIZE(DateText)-1;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); + if (ChPtr==NULL || QuoteMode) + { + DateText[J]=Mask[I]; +#ifdef _WIN_ALL + // We do not allow ':' in Windows because of NTFS streams. + // Users had problems after specifying hh:mm mask. + if (DateText[J]==':') + DateText[J]='_'; +#endif + } + else + { + size_t FieldPos=ChPtr-MaskChars; + int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--; + + // CField[FieldPos] shall have exactly 3 "MMM" symbols, so we do not + // repeat the month name in case "MMMMMMMM" mask. But since we + // decremented CField[FieldPos] above, we compared it with 2. + if (FieldPos==1 && CField[FieldPos]==2 && + toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M') + { + wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J); + J=wcslen(DateText); + I+=2; + continue; + } + // If CharPos is negative, we have more modifier characters than + // matching time data. We prefer to issue a modifier character + // instead of repeating time data from beginning, so user can notice + // excessive modifiers added by mistake. + if (CharPos<0) + DateText[J]=Mask[I]; + else + DateText[J]=Field[FieldPos][CharPos]; + } + DateText[++J]=0; + } + + if (Prefix) + { + wchar NewName[NM]; + GetFilePath(ArcName,NewName,ASIZE(NewName)); + AddEndSlash(NewName,ASIZE(NewName)); + wcsncatz(NewName,DateText,ASIZE(NewName)); + wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName)); + wcsncpyz(ArcName,NewName,MaxSize); + } + else + wcsncatz(ArcName,DateText,MaxSize); + wcsncatz(ArcName,Ext,MaxSize); +} + + +void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving) +{ + wchar NewName[NM]; + + uint ArcNumber=1; + while (true) // Loop for 'N' (archive number) processing. + { + wcsncpyz(NewName,ArcName,ASIZE(NewName)); + + bool ArcNumPresent=false; + + GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber,ArcNumPresent); + + if (!ArcNumPresent) + break; + if (!FileExist(NewName)) + { + if (!Archiving && ArcNumber>1) + { + // 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,ASIZE(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; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path". + return true; + } + else + if (Src[0]=='\\' && Src[1]=='\\') + { + if (MaxSize<=PrefixLength+SrcLength+2) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,L"UNC",MaxSize); + wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share". + 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; + wcsncpyz(Dest,Prefix,MaxSize); + CurDir[2]=0; + wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'. + wcsncatz(Dest,Src,MaxSize); + return true; + } + else // Paths in path\name format. + { + AddEndSlash(CurDir,ASIZE(CurDir)); + if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,CurDir,MaxSize); + + 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); + } +} + + +void MakeNameCompatible(wchar *Name,size_t MaxSize) +{ + // Remove trailing spaces and dots in file name and in dir names in path. + 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--) + { + // Permit path1/./path2 and ../path1 paths. + if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) + break; + Dest--; + } + Name[Dest]=Name[Src]; + if (Name[Src]==0) + break; + Src++; + Dest++; + } + + // Rename reserved device names, such as aux.txt to _aux.txt. + // We check them in path components too, where they are also prohibited. + for (uint I=0;Name[I]!=0;I++) + if (I==0 || I>0 && IsPathDiv(Name[I-1])) + { + static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"}; + wchar *s=Name+I; + bool MatchFound=false; + for (uint J=0;J<ASIZE(Devices);J++) + for (uint K=0;;K++) + if (Devices[J][K]=='#') + { + if (!IsDigit(s[K])) + break; + } + else + if (Devices[J][K]==0) + { + // Names like aux.txt are accessible without \\?\ prefix + // since Windows 11. Pure aux is still prohibited. + MatchFound=s[K]==0 || s[K]=='.' && !IsWindows11OrGreater() || IsPathDiv(s[K]); + break; + } + else + if (Devices[J][K]!=toupperw(s[K])) + break; + if (MatchFound) + { + wchar OrigName[NM]; + wcsncpyz(OrigName,Name,ASIZE(OrigName)); + if (MaxSize>I+1) // I+1, because we do not move the trailing 0. + memmove(s+1,s,(MaxSize-I-1)*sizeof(*s)); + *s='_'; +#ifndef SFX_MODULE + uiMsg(UIMSG_CORRECTINGNAME,nullptr); + uiMsg(UIERROR_RENAMING,nullptr,OrigName,Name); +#endif + } + } +} +#endif + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.hpp new file mode 100644 index 00000000..efb28330 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/pathfn.hpp @@ -0,0 +1,77 @@ +#ifndef _RAR_PATHFN_ +#define _RAR_PATHFN_ + +wchar* PointToName(const wchar *Path); +wchar* PointToLastChar(const wchar *Path); +wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize); +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,size_t MaxSize); +#endif + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/qopen.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/qopen.cpp new file mode 100644 index 00000000..43346b06 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/qopen.cpp @@ -0,0 +1,300 @@ +#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 for the first time, perform additional intialization. + SeekPos=Arc->Tell(); + UnsyncSeekPos=false; + + int64 SavePos=SeekPos; + Arc->Seek(BlockPos,SEEK_SET); + + // If BlockPos points to original main header, we'll have the infinite + // recursion, because ReadHeader() for main header will attempt to load + // QOpen and call QuickOpen::Load again. If BlockPos points to long chain + // of other main headers, we'll have multiple recursive calls of this + // function wasting resources. So we prohibit QOpen temporarily to + // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator + // and QOpenOffset fields, so we cannot use them to prohibit QOpen. + Arc->SetProhibitQOpen(true); + size_t ReadSize=Arc->ReadHeader(); + Arc->SetProhibitQOpen(false); + + if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || + !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) + { + Arc->Seek(SavePos,SEEK_SET); + return; + } + QOHeaderPos=Arc->CurBlockPos; + RawDataStart=Arc->Tell(); + RawDataSize=Arc->SubHead.UnpSize; + Arc->Seek(SavePos,SEEK_SET); + + 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 + { + Loaded=false; + 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)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos) + Load(QOHeaderPos); + + if (Method==SEEK_SET) + SeekPos=Offset; + if (Method==SEEK_CUR) + SeekPos+=Offset; + UnsyncSeekPos=true; + + if (Method==SEEK_END) + { + Arc->File::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() +{ + int64 SavePos=Arc->Tell(); + 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; + int ReadSize=0; + if (SizeToRead!=0) + { + ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); + if (ReadSize<=0) + ReadSize=0; + else + { +#ifndef RAR_NOCRYPT + if (Arc->SubHead.Encrypted) + Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); +#endif + RawDataPos+=ReadSize; + ReadBufSize+=ReadSize; + } + } + Arc->Seek(SavePos,SEEK_SET); + 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 Buf boundary, read it in loop. + while (SizeToRead>0) + { + size_t DataLeft=ReadBufSize-ReadBufPos; + 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(); + if (HeaderSize>MAX_HEADER_SIZE_RAR5) + return false; + LastReadHeader.Alloc(HeaderSize); + Raw.GetB(&LastReadHeader[0],HeaderSize); + // Calculate the absolute position as offset from quick open service header. + LastReadHeaderPos=QOHeaderPos-Offset; + return true; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/qopen.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/qopen.hpp new file mode 100644 index 00000000..d745cea8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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; // Read quick open data here. + static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE. + size_t CurBufSize; // Current size of buffered data in write mode. +#ifndef RAR_NOCRYPT // For shell extension. + CryptData Crypt; +#endif + + bool Loaded; + uint64 QOHeaderPos; // Main QO header position. + uint64 RawDataStart; // Start of QO data, just after the main header. + uint64 RawDataSize; // Size of entire QO data. + uint64 RawDataPos; // Current read position in QO data. + size_t ReadBufSize; // Size of Buf data currently read from QO. + size_t ReadBufPos; // Current read position in Buf data. + Array<byte> 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/Src/external_dependencies/openmpt-trunk/include/unrar/rar.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rar.cpp new file mode 100644 index 00000000..34b4b278 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rar.cpp @@ -0,0 +1,107 @@ +#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. + POWER_MODE ShutdownOnClose=POWERMODE_KEEP; +#endif + + try + { + + CommandData *Cmd=new CommandData; +#ifdef SFX_MODULE + wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command)); + 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; + if (ShutdownOnClose) + ShutdownCheckAnother(true); +#endif + + uiInit(Cmd->Sound); + 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!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() && + !ShutdownCheckAnother(false)) + Shutdown(ShutdownOnClose); +#endif + ErrHandler.MainExit=true; + return ErrHandler.GetErrorCode(); +} +#endif + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rar.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rar.hpp new file mode 100644 index 00000000..20e32fb2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rar.hpp @@ -0,0 +1,96 @@ +#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 "strlist.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" +#ifdef _WIN_ALL +#include "isnt.hpp" +#endif +#include "file.hpp" +#include "crc.hpp" +#include "ui.hpp" +#include "filefn.hpp" +#include "filestr.hpp" +#include "find.hpp" +#include "scantree.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" +#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/Src/external_dependencies/openmpt-trunk/include/unrar/rardefs.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rardefs.hpp new file mode 100644 index 00000000..095792a0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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. +#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/Src/external_dependencies/openmpt-trunk/include/unrar/rarlang.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rarlang.hpp new file mode 100644 index 00000000..6151d15a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/raros.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/raros.hpp new file mode 100644 index 00000000..4f4f2ae7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/rarpch.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rarpch.cpp new file mode 100644 index 00000000..c070cf74 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/rartypes.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rartypes.hpp new file mode 100644 index 00000000..3d3111bc --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rartypes.hpp @@ -0,0 +1,32 @@ +#ifndef _RAR_TYPES_ +#define _RAR_TYPES_ + +#include <stdint.h> + +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)) + +// Maximum int64 value. +#define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff)) + +// Special int64 value, large enough to never be found in real life +// and small enough to fit to both signed and unsigned 64-bit ints. +// 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 if 64 bit type is not supported. +#define INT64NDF INT32TO64(0x7fffffff,0x7fffffff) + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rarvm.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rarvm.cpp new file mode 100644 index 00000000..8d8675a3 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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;I<CodeSize;I++) + XorSum^=Code[I]; + + if (XorSum!=Code[0]) + return; + + struct StandardFilters + { + uint Length; + uint CRC; + VM_StandardFilters Type; + } static StdList[]={ + 53, 0xad576887, VMSF_E8, + 57, 0x3cd7e57e, VMSF_E8E9, + 120, 0x3769893f, VMSF_ITANIUM, + 29, 0x0e06077d, VMSF_DELTA, + 149, 0x1c2c5dc8, VMSF_RGB, + 216, 0xbc85e701, VMSF_AUDIO + }; + uint CodeCRC=CRC32(0xffffffff,Code,CodeSize)^0xffffffff; + for (uint I=0;I<ASIZE(StdList);I++) + if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize) + { + Prg->Type=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 (Pos<VM_MEMSIZE && Data!=Mem+Pos) + { + // We can have NULL Data for invalid filters with DataSize==0. While most + // sensible memmove implementations do not care about data if size is 0, + // let's follow the standard and check the size first. + size_t CopySize=Min(DataSize,VM_MEMSIZE-Pos); + if (CopySize!=0) + memmove(Mem+Pos,Data,CopySize); + } +} + + +bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) +{ + switch(FilterType) + { + case VMSF_E8: + case VMSF_E8E9: + { + byte *Data=Mem; + uint DataSize=R[4],FileOffset=R[6]; + + if (DataSize>VM_MEMSIZE || DataSize<4) + return false; + + const uint FileSize=0x1000000; + byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; + for (uint CurPos=0;CurPos<DataSize-4;) + { + byte CurByte=*(Data++); + CurPos++; + if (CurByte==0xe8 || CurByte==CmpByte2) + { + uint Offset=CurPos+FileOffset; + uint Addr=RawGet4(Data); + + // We check 0x80000000 bit instead of '< 0' comparison + // not assuming int32 presence or uint size and endianness. + if ((Addr & 0x80000000)!=0) // Addr<0 + { + if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0 + RawPut4(Addr+FileSize,Data); + } + else + if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize + RawPut4(Addr-Offset,Data); + Data+=4; + CurPos+=4; + } + } + } + break; + case VMSF_ITANIUM: + { + byte *Data=Mem; + uint DataSize=R[4],FileOffset=R[6]; + + if (DataSize>VM_MEMSIZE || DataSize<21) + return false; + + uint CurPos=0; + + FileOffset>>=4; + + while (CurPos<DataSize-21) + { + int Byte=(Data[0]&0x1f)-0x10; + if (Byte>=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<<I)) + { + uint StartPos=I*41+5; + uint OpType=FilterItanium_GetBits(Data,StartPos+37,4); + if (OpType==5) + { + uint Offset=FilterItanium_GetBits(Data,StartPos+13,20); + FilterItanium_SetBits(Data,(Offset-FileOffset)&0xfffff,StartPos+13,20); + } + } + } + Data+=16; + CurPos+=16; + FileOffset++; + } + } + break; + case VMSF_DELTA: + { + uint DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2; + if (DataSize>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;CurChannel<Channels;CurChannel++) + { + byte PrevByte=0; + for (uint DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels) + Mem[DestPos]=(PrevByte-=Mem[SrcPos++]); + } + } + break; + case VMSF_RGB: + { + uint DataSize=R[4],Width=R[0]-3,PosR=R[1]; + if (DataSize>VM_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<Channels;CurChannel++) + { + uint PrevByte=0; + + for (uint I=CurChannel;I<DataSize;I+=Channels) + { + uint Predicted; + if (I>=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;I<Border;I+=3) + { + byte G=DestData[I+1]; + DestData[I]+=G; + DestData[I+2]+=G; + } + } + break; + case VMSF_AUDIO: + { + uint DataSize=R[4],Channels=R[0]; + byte *SrcData=Mem,*DestData=SrcData+DataSize; + // In fact, audio channels never exceed 4. + if (DataSize>VM_MEMSIZE/2 || Channels>128 || Channels==0) + return false; + for (uint CurChannel=0;CurChannel<Channels;CurChannel++) + { + uint PrevByte=0,PrevDelta=0,Dif[7]; + int D1=0,D2=0,D3; + int K1=0,K2=0,K3=0; + memset(Dif,0,sizeof(Dif)); + + for (uint I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++) + { + D3=D2; + D2=PrevDelta-D1; + D1=PrevDelta; + + uint Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3; + Predicted=(Predicted>>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<ASIZE(Dif);J++) + { + if (Dif[J]<MinDif) + { + MinDif=Dif[J]; + NumMinDif=J; + } + Dif[J]=0; + } + switch(NumMinDif) + { + case 1: if (K1>=-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<<InBit); + + BitField<<=InBit; + + for (uint I=0;I<4;I++) + { + Data[InAddr+I]&=AndMask; + Data[InAddr+I]|=BitField; + AndMask=(AndMask>>8)|0xff000000; + BitField>>=8; + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rarvm.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rarvm.hpp new file mode 100644 index 00000000..e65c4b1a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/rawint.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rawint.hpp new file mode 100644 index 00000000..30379888 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/rawread.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rawread.cpp new file mode 100644 index 00000000..d99bac84 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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 ReadPos<DataSize ? Data[ReadPos++]:0; +} + + +ushort RawRead::Get2() +{ + if (ReadPos+1<DataSize) + { + ushort Result=Data[ReadPos]+(Data[ReadPos+1]<<8); + ReadPos+=2; + return Result; + } + return 0; +} + + +uint RawRead::Get4() +{ + if (ReadPos+3<DataSize) + { + uint Result=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+ + (Data[ReadPos+3]<<24); + ReadPos+=4; + return Result; + } + return 0; +} + + +uint64 RawRead::Get8() +{ + uint Low=Get4(),High=Get4(); + return INT32TO64(High,Low); +} + + +uint64 RawRead::GetV() +{ + uint64 Result=0; + // Need to check Shift<64, because for shift greater than or equal to + // the width of the promoted left operand, the behavior is undefined. + for (uint Shift=0;ReadPos<DataSize && Shift<64;Shift+=7) + { + byte CurByte=Data[ReadPos++]; + Result+=uint64(CurByte & 0x7f)<<Shift; + if ((CurByte & 0x80)==0) + return Result; // Decoded successfully. + } + return 0; // Out of buffer border. +} + + +// Return a number of bytes in current variable length integer. +uint RawRead::GetVSize(size_t Pos) +{ + for (size_t CurPos=Pos;CurPos<DataSize;CurPos++) + if ((Data[CurPos] & 0x80)==0) + return int(CurPos-Pos+1); + return 0; // Buffer overflow. +} + + +size_t RawRead::GetB(void *Field,size_t Size) +{ + byte *F=(byte *)Field; + size_t CopySize=Min(DataSize-ReadPos,Size); + if (CopySize>0) + 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<DataSize) + { + RawToWide(&Data[ReadPos],Field,Size); + ReadPos+=sizeof(wchar)*Size; + } + else + memset(Field,0,sizeof(wchar)*Size); +} + + +uint RawRead::GetCRC15(bool ProcessedOnly) // RAR 1.5 block CRC. +{ + if (DataSize<=2) + return 0; + uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2); + return ~HeaderCRC & 0xffff; +} + + +uint RawRead::GetCRC50() // RAR 5.0 block CRC. +{ + if (DataSize<=4) + return 0xffffffff; + return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff; +} + + +// Read vint from arbitrary byte array. +uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow) +{ + Overflow=false; + uint64 Result=0; + for (uint Shift=0;ReadPos<DataSize;Shift+=7) + { + byte CurByte=Data[ReadPos++]; + Result+=uint64(CurByte & 0x7f)<<Shift; + if ((CurByte & 0x80)==0) + return Result; // Decoded successfully. + } + Overflow=true; + return 0; // Out of buffer border. +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rawread.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rawread.hpp new file mode 100644 index 00000000..b3198987 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rawread.hpp @@ -0,0 +1,41 @@ +#ifndef _RAR_RAWREAD_ +#define _RAR_RAWREAD_ + +class RawRead +{ + private: + Array<byte> 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/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.cpp new file mode 100644 index 00000000..99407a86 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.cpp @@ -0,0 +1,325 @@ +#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; + UnpPackedLeft=0; + ShowProgress=true; + TestMode=false; + SkipUnpCRC=false; + NoFileHeader=false; + PackVolume=false; + UnpVolume=false; + NextVolumeMissing=false; + SrcFile=NULL; + DestFile=NULL; + UnpWrAddr=NULL; + UnpWrSize=0; + Command=NULL; + Encryption=false; + Decryption=false; + CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0; + LastPercent=-1; + SubHead=NULL; + SubHeadPos=NULL; + CurrentCommand=0; + ProcessedArcSize=0; + LastArcSize=0; + 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>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count; + if (SizeToRead > 0) + { + if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft) + { + // 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 (!NoFileHeader && 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 + UnpPackedLeft-=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 && UnpPackedLeft == 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->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize); + if (ReadSize!=-1) + { + ReadSize=TotalRead; +#ifndef RAR_NOCRYPT + if (Decryption) + Decrypt->DecryptBlock(Addr,ReadSize); +#endif + } + Wait(); + return ReadSize; +} + + +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); + /* // OPENMPT ADDITION + if (Cmd->ProcessDataProc!=NULL) + { + int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); + if (RetCode==0) + ErrHandler.Exit(RARX_USERBREAK); + } + */ // OPENMPT ADDITION + } +#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(); +} + + + + + + +void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) +{ + return; // OPENMPT ADDITION + if (ShowProgress && SrcFile!=NULL) + { + // Important when processing several archives or multivolume archive. + 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; +} + + +// Extraction progress is based on the position in archive and we adjust +// the total archives size here, so trailing blocks do not prevent progress +// reaching 100% at the end of extraction. Alternatively we could print "100%" +// after completing the entire archive extraction, but then we would need +// to take into account possible messages like the checksum error after +// last file percent progress. +void ComprDataIO::AdjustTotalArcSize(Archive *Arc) +{ + // If we know a position of QO or RR blocks, use them to adjust the total + // packed size to beginning of these blocks. Earlier we already calculated + // the total size based on entire archive sizes. We also set LastArcSize + // to start of first trailing block, to add it later to ProcessedArcSize. + int64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; + if (Arc->MainHead.QOpenOffset!=0) // QO is always preceding RR record. + LastArcSize=Arc->MainHead.QOpenOffset; + else + if (Arc->MainHead.RROffset!=0) + LastArcSize=Arc->MainHead.RROffset; + else + { + // If neither QO nor RR are found, exclude the approximate size of + // end of archive block. + // We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC, + // but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file + // name, so we do not have two files with 100% at the end of archive. + const uint EndBlock=23; + + if (ArcLength>EndBlock) + LastArcSize=ArcLength-EndBlock; + } + + TotalArcSize-=ArcLength-LastArcSize; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.hpp new file mode 100644 index 00000000..3060a0ff --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rdwrfn.hpp @@ -0,0 +1,105 @@ +#ifndef _RAR_DATAIO_ +#define _RAR_DATAIO_ + +class Archive; +class CmdAdd; +class Unpack; +class ArcFileSearch; + +#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; + int64 UnpPackedLeft; + + bool ShowProgress; + bool TestMode; + bool SkipUnpCRC; + bool NoFileHeader; + + 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=UnpPackedLeft=Size;} + void SetTestMode(bool Mode) {TestMode=Mode;} + void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;} + void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;} + 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;} + void AdjustTotalArcSize(Archive *Arc); + + + bool PackVolume; + bool UnpVolume; + bool NextVolumeMissing; + int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; + + + // Size of already processed archives. + // Used to calculate the total operation progress. + int64 ProcessedArcSize; + + // Last extracted archive size up to QO or RR block. + int64 LastArcSize; + + 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/Src/external_dependencies/openmpt-trunk/include/unrar/readme.txt b/Src/external_dependencies/openmpt-trunk/include/unrar/readme.txt new file mode 100644 index 00000000..9ca03f72 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/recvol.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol.cpp new file mode 100644 index 00000000..adf58404 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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(Cmd,false); + return RecVol.Restore(Cmd,Name,Silent); + } + else + { + RecVolumes5 RecVol(Cmd,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(Cmd,true); + RecVol.Test(Cmd,Name); + } + else + { + RecVolumes3 RecVol(Cmd,true); + RecVol.Test(Cmd,Name); + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/recvol.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol.hpp new file mode 100644 index 00000000..06510a21 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol.hpp @@ -0,0 +1,88 @@ +#ifndef _RAR_RECVOL_ +#define _RAR_RECVOL_ + +#define REV5_SIGN "Rar!\x1aRev" +#define REV5_SIGN_SIZE 8 + +class RecVolumes3 +{ + private: + File *SrcFile[256]; + Array<byte> Buf; + +#ifdef RAR_SMP + ThreadPool *RSThreadPool; +#endif + public: + RecVolumes3(RAROptions *Cmd,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<RecVolItem> 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 + uint MaxUserThreads; // Maximum number of threads defined by user. + RecRSThreadData *ThreadData; // Array to store thread parameters. + public: // 'public' only because called from thread functions. + void ProcessAreaRS(RecRSThreadData *td); + public: + RecVolumes5(RAROptions *Cmd,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/Src/external_dependencies/openmpt-trunk/include/unrar/recvol3.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol3.cpp new file mode 100644 index 00000000..9fb846a2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol3.cpp @@ -0,0 +1,544 @@ +// 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(RAROptions *Cmd,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=new ThreadPool(Cmd->Threads); +#endif + } +} + + +RecVolumes3::~RecVolumes3() +{ + for (size_t I=0;I<ASIZE(SrcFile);I++) + delete SrcFile[I]; +#ifdef RAR_SMP + delete RSThreadPool; +#endif +} + + + + +void RSEncode::EncodeBuf() +{ + for (int BufPos=BufStart;BufPos<BufEnd;BufPos++) + { + byte Data[256],Code[256]; + for (int I=0;I<FileNumber;I++) + Data[I]=Buf[I*RecBufferSize+BufPos]; + RSC.Encode(Data,FileNumber,Code); + for (int I=0;I<RecVolNumber;I++) + OutBuf[I*RecBufferSize+BufPos]=Code[I]; + } +} + + +// Check for names like arc5_3_1.rev created by RAR 3.0. +static bool IsNewStyleRev(const wchar *Name) +{ + wchar *Ext=GetExt(Name); + if (Ext==NULL) + return true; + int DigitGroup=0; + for (Ext--;Ext>Name;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--; + wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName)); + + 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<ASIZE(P);I++) + { + do + { + Dot--; + } while (IsDigit(*Dot) && Dot>=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]; + wcsncpyz(PrevName,CurName,ASIZE(PrevName)); + 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;CurArcNum<FileNumber;CurArcNum++) + { + Archive *NewFile=new Archive(Cmd); + bool ValidVolume=FileExist(ArcName); + if (ValidVolume) + { + NewFile->TOpen(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]; + wcsncpyz(NewName,ArcName,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + 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) + wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName)); + + 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;I<TotalFiles;I++) + if (WriteFlags[I] || SrcFile[I]==NULL) + Erasures[EraSize++]=I; + + int64 ProcessedSize=0; + int LastPercent=-1; + mprintf(L" "); + // Size of per file buffer. + size_t RecBufferSize=TotalBufferSize/TotalFiles; + +#ifdef RAR_SMP + uint ThreadNumber=Cmd->Threads; +#else + uint ThreadNumber=1; +#endif + RSEncode *rse=new RSEncode[ThreadNumber]; + for (uint I=0;I<ThreadNumber;I++) + rse[I].Init(RecVolNumber); + + while (true) + { + Wait(); + int MaxRead=0; + for (int I=0;I<TotalFiles;I++) + if (WriteFlags[I] || SrcFile[I]==NULL) + memset(&Buf[I*RecBufferSize],0,RecBufferSize); + else + { + int ReadSize=SrcFile[I]->Read(&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;BlockStart<MaxRead;CurThread++) + { + // Last thread processes all left data including increasement + // from rounding error. + if (CurThread==ThreadNumber-1) + BlockSize=MaxRead-BlockStart; + + RSEncode *curenc=rse+CurThread; + curenc->Buf=&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;I<FileNumber;I++) + if (WriteFlags[I]) + SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead); + } + delete[] rse; + + for (int I=0;I<RecVolNumber+FileNumber;I++) + if (SrcFile[I]!=NULL) + { + File *CurFile=SrcFile[I]; + if (NewStyle && WriteFlags[I]) + { + int64 Length=CurFile->Tell(); + 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 (ZeroCount<ReadSize && Buf[ZeroCount]==0) + ZeroCount++; + if (ZeroCount==ReadSize) + { + Arc.Seek(Arc.NextBlockPos,SEEK_SET); + Arc.Truncate(); + } + } + } +#if !defined(SILENT) + if (!Cmd->DisablePercentage) + mprintf(L"\b\b\b\b100%%"); + if (!Silent && !Cmd->DisableDone) + mprintf(St(MDone)); +#endif + return true; +} + + +void RSEncode::DecodeBuf() +{ + for (int BufPos=BufStart;BufPos<BufEnd;BufPos++) + { + byte Data[256]; + for (int I=0;I<FileNumber;I++) + Data[I]=Buf[I*RecBufferSize+BufPos]; + RSC.Decode(Data,FileNumber,Erasures,EraSize); + for (int I=0;I<EraSize;I++) + Buf[Erasures[I]*RecBufferSize+BufPos]=Data[Erasures[I]]; + } +} + + +void RecVolumes3::Test(RAROptions *Cmd,const wchar *Name) +{ + if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32. + { + ErrHandler.UnknownMethodMsg(Name,Name); + return; + } + + wchar VolName[NM]; + wcsncpyz(VolName,Name,ASIZE(VolName)); + + while (FileExist(VolName)) + { + File CurFile; + if (!CurFile.Open(VolName)) + { + ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN. + continue; + } + if (!uiStartFileExtract(VolName,false,true,false)) + return; + mprintf(St(MExtrTestFile),VolName); + mprintf(L" "); + CurFile.Seek(0,SEEK_END); + int64 Length=CurFile.Tell(); + CurFile.Seek(Length-4,SEEK_SET); + uint FileCRC=0; + for (int I=0;I<4;I++) + FileCRC|=CurFile.GetByte()<<(I*8); + + uint CalcCRC; + CalcFileSum(&CurFile,&CalcCRC,NULL,1,Length-4,Cmd->DisablePercentage ? 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/Src/external_dependencies/openmpt-trunk/include/unrar/recvol5.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol5.cpp new file mode 100644 index 00000000..596ea06f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/recvol5.cpp @@ -0,0 +1,538 @@ +static const uint MaxVolumes=65535; + +// We select this limit arbitrarily, to prevent user creating too many +// rev files by mistake. +#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. + +RecVolumes5::RecVolumes5(RAROptions *Cmd,bool TestOnly) +{ + RealBuf=NULL; + RealReadBuffer=NULL; + + DataCount=0; + RecCount=0; + TotalCount=0; + RecBufferSize=0; + +#ifdef RAR_SMP + MaxUserThreads=Cmd->Threads; +#else + MaxUserThreads=1; +#endif + + ThreadData=new RecRSThreadData[MaxUserThreads]; + for (uint I=0;I<MaxUserThreads;I++) + { + ThreadData[I].RecRSPtr=this; + ThreadData[I].RS=NULL; + } + + if (TestOnly) + { +#ifdef RAR_SMP + RecThreadPool=NULL; +#endif + } + else + { +#ifdef RAR_SMP + RecThreadPool=new ThreadPool(MaxUserThreads); +#endif + RealBuf=new byte[TotalBufferSize+SSE_ALIGNMENT]; + Buf=(byte *)ALIGN_VALUE(RealBuf,SSE_ALIGNMENT); + } +} + + +RecVolumes5::~RecVolumes5() +{ + delete[] RealBuf; + delete[] RealReadBuffer; + for (uint I=0;I<RecItems.Size();I++) + delete RecItems[I].f; + for (uint I=0;I<MaxUserThreads;I++) + delete ThreadData[I].RS; + delete[] ThreadData; +#ifdef RAR_SMP + delete RecThreadPool; +#endif +} + + + + +#ifdef RAR_SMP +THREAD_PROC(RecThreadRS) +{ + RecRSThreadData *td=(RecRSThreadData *)Data; + td->RecRSPtr->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;I<Count;I++) + RS.UpdateECC(DataNum, I, Data, Buf+I*RecBufferSize, MaxRead); +*/ + + uint ThreadNumber=MaxUserThreads; + + 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 (ThreadDataSize<MinThreadBlock) + ThreadDataSize=MinThreadBlock; + + for (size_t I=0,CurPos=0;I<ThreadNumber && CurPos<MaxRead;I++) + { + RecRSThreadData *td=ThreadData+I; + if (td->RS==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;I<Count;I++) + td->RS->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--; + if (Num<=PointToName(ArcName)) + return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. + wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); + + wchar FirstVolName[NM]; + *FirstVolName=0; + + wchar LongestRevName[NM]; + *LongestRevName=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 (!fd.IsDir && 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++; + + if (wcslen(fd.Name)>wcslen(LongestRevName)) + wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName)); + } + } + 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;I<VolNum;I++) + RecItems[I].f=NULL; + } + ItemPos=VolNum-1; + + if (*FirstVolName==0) + VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true); + } + } + if (ItemPos==-1) + delete Vol; // Skip found file, it is not RAR or REV volume. + else + if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed. + { + // Store found RAR or REV volume. + RecVolItem *Item=RecItems+ItemPos; + Item->f=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; + + // If we did not find even a single .rar volume, create .rar volume name + // based on the longest .rev file name. Use longest .rev, so we have + // enough space for volume number. + if (*FirstVolName==0) + { + SetExt(LongestRevName,L"rar",ASIZE(LongestRevName)); + VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true); + } + + uiMsg(UIMSG_RECVOLCALCCHECKSUM); + + MissingVolumes=0; + for (uint I=0;I<TotalCount;I++) + { + RecVolItem *Item=&RecItems[I]; + if (Item->f!=NULL) + { + uiMsg(UIMSG_STRING,Item->Name); + + uint RevCRC; + CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,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 (I<DataCount && (Item->f==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;I<DataCount;I++) + { + RecVolItem *Item=&RecItems[I]; + if (Item->FileSize>MaxVolSize) + MaxVolSize=Item->FileSize; + if (Item->f!=NULL && !Item->Valid) + { + Item->f->Close(); + + wchar NewName[NM]; + wcsncpyz(NewName,Item->Name,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + 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))==true) + { + 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; + } + 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;I<TotalCount;I++) + { + ValidFlags[I]=RecItems[I].f!=NULL && !RecItems[I].New; + if (I<DataCount && !ValidFlags[I]) + MissingVolumes++; + } + + // Size of per file buffer. + RecBufferSize=TotalBufferSize/MissingVolumes; + if ((RecBufferSize&1)==1) // Must be even for our RS16 codec. + RecBufferSize--; +#ifdef USE_SSE + RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE. +#endif + + RSCoder16 RS; + if (!RS.Init(DataCount,RecCount,ValidFlags)) + { + uiMsg(UIERROR_OPFAILED); + delete[] ValidFlags; + return false; // Should not happen, we check parameter validity above. + } + + RealReadBuffer=new byte[RecBufferSize+SSE_ALIGNMENT]; + byte *ReadBuf=(byte *)ALIGN_VALUE(RealReadBuffer,SSE_ALIGNMENT); + + while (true) + { + Wait(); + + int MaxRead=0; + for (uint I=0,J=DataCount;I<DataCount;I++) + { + uint VolNum=I; + if (!ValidFlags[I]) // If next RAR volume is missing or invalid. + { + while (!ValidFlags[J]) // Find next valid REV volume. + J++; + VolNum=J++; // Use next valid REV volume data instead of RAR. + } + RecVolItem *Item=RecItems+VolNum; + + byte *B=&ReadBuf[0]; + int ReadSize=0; + if (Item->f!=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;I<DataCount;I++) + if (!ValidFlags[I]) + { + RecVolItem *Item=RecItems+I; + size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize); + 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;I<TotalCount;I++) + if (RecItems[I].f!=NULL) + RecItems[I].f->Close(); + + delete[] ValidFlags; +#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;I<TotalCount;I++) + RecItems[I].f=NULL; + for (uint I=0;I<DataCount;I++) + { + RecItems[I].FileSize=Raw.Get8(); + RecItems[I].CRC=Raw.Get4(); + } + } + + RecItems[RecNum].CRC=RevCRC; // Assign it here, after allocating RecItems. + + return RecNum; +} + + +void RecVolumes5::Test(RAROptions *Cmd,const wchar *Name) +{ + wchar VolName[NM]; + wcsncpyz(VolName,Name,ASIZE(VolName)); + + uint FoundRecVolumes=0; + while (FileExist(VolName)) + { + File CurFile; + if (!CurFile.Open(VolName)) + { + ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN. + continue; + } + if (!uiStartFileExtract(VolName,false,true,false)) + return; + mprintf(St(MExtrTestFile),VolName); + mprintf(L" "); + bool Valid=false; + uint RecNum=ReadHeader(&CurFile,FoundRecVolumes==0); + if (RecNum!=0) + { + FoundRecVolumes++; + + uint RevCRC; + CalcFileSum(&CurFile,&RevCRC,NULL,1,INT64NDF,CALCFSUM_CURPOS|(Cmd->DisablePercentage ? 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/Src/external_dependencies/openmpt-trunk/include/unrar/resource.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/resource.cpp new file mode 100644 index 00000000..dadd0723 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/resource.cpp @@ -0,0 +1,22 @@ +#include "rar.hpp" + + + + + +#ifndef RARDLL +const wchar* St(MSGID StringId) +{ + return StringId; +} + + +// Needed for Unix swprintf to convert %s to %ls in legacy language resources. +const wchar *StF(MSGID StringId) +{ + static wchar FormattedStr[512]; + PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr)); + return FormattedStr; +} +#endif + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/resource.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/resource.hpp new file mode 100644 index 00000000..62c5bf49 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/resource.hpp @@ -0,0 +1,13 @@ +#ifndef _RAR_RESOURCE_ +#define _RAR_RESOURCE_ + +#ifdef RARDLL +#define St(x) (L"") +#define StF(x) (L"") +#else +const wchar *St(MSGID StringId); +const wchar *StF(MSGID StringId); +#endif + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.cpp new file mode 100644 index 00000000..48ac1723 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.cpp @@ -0,0 +1,523 @@ +/************************************************************************** + * This code is based on Szymon Stefanek public domain AES implementation * + **************************************************************************/ +#include "rar.hpp" + +#ifdef USE_SSE +#include <wmmintrin.h> +#endif + +static byte S[256]= +{ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 +}; + +static byte S5[256]; + +// Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256. +static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36}; + +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 (S5[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, 0x80000000); // Get the maximum supported cpuid function. + if ((CPUInfo[0] & 0x7fffffff)>=1) + { + __cpuid(CPUInfo, 1); + AES_NI=(CPUInfo[2] & 0x2000000)!=0; + } + else + AES_NI=false; +#endif + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + uint uKeyLenInBytes=0; + + 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<m_uRounds; i++) + { + __m128i ri = _mm_loadu_si128(rkey + i); + v = _mm_aesenc_si128(v, ri); + } + + __m128i rl = _mm_loadu_si128(rkey + m_uRounds); + v = _mm_aesenclast_si128(v, rl); + _mm_storeu_si128(dest++,v); + numBlocks--; + } + _mm_storeu_si128((__m128i*)m_initVector,v); +} +#endif + + +void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer) +{ + if (inputLen <= 0) + return; + + size_t numBlocks=inputLen/16; +#ifdef USE_SSE + if (AES_NI) + { + blockDecryptSSE(input,numBlocks,outBuffer); + return; + } +#endif + + byte block[16], iv[4][4]; + memcpy(iv,m_initVector,16); + + for (size_t i = numBlocks; 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])); + } +} + + +static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication. +{ + const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial. + byte result = 0; + while (b>0) + { + if ((b & 1) != 0) + result ^= a; + a = (a & 0x80) ? (a<<1)^poly : a<<1; + b >>= 1; + } + return result; +} + + +// 2021-09-24: changed to slower and simpler code without interim tables. +// It is still fast enough for our purpose. +void Rijndael::GenerateTables() +{ + for (int I=0;I<256;I++) + S5[S[I]]=I; + + for (int I=0;I<256;I++) + { + byte s=S[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]=s; + T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2); + T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3); + + byte b=S5[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]=gmul(b,0xb); + U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9); + U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd); + U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe); + } +} + + +#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<sizeof(PT);I+=16) + rij.blockEncrypt(PT+I,16,Out); + BinToHex(Chk[L],16,NULL,Str,ASIZE(Str)); + mprintf(L"\nAES-%d expected: %s",KeyLength,Str); + BinToHex(Out,sizeof(Out),NULL,Str,ASIZE(Str)); + mprintf(L"\nAES-%d result: %s",KeyLength,Str); + if (memcmp(Out,Chk[L],16)==0) + mprintf(L" OK"); + else + { + mprintf(L" FAILED"); + getchar(); + } + } +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.hpp new file mode 100644 index 00000000..797899ff --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rijndael.hpp @@ -0,0 +1,40 @@ +#ifndef _RIJNDAEL_H_ +#define _RIJNDAEL_H_ + +/************************************************************************** + * This code is based on Szymon Stefanek public domain AES implementation * + **************************************************************************/ + +#define _MAX_KEY_COLUMNS (256/32) +#define _MAX_ROUNDS 14 +#define MAX_IV_SIZE 16 + +class Rijndael +{ + private: +#ifdef USE_SSE + void blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer); + void blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer); + + bool AES_NI; +#endif + void keySched(byte key[_MAX_KEY_COLUMNS][4]); + void keyEncToDec(); + void GenerateTables(); + + // RAR always uses CBC, but we may need to turn it off when calling + // this code from other archive formats with CTR and other modes. + bool CBCMode; + + int m_uRounds; + byte m_initVector[MAX_IV_SIZE]; + byte m_expandedKey[_MAX_ROUNDS+1][4][4]; + public: + Rijndael(); + void Init(bool Encrypt,const byte *key,uint keyLen,const byte *initVector); + void blockEncrypt(const byte *input, size_t inputLen, byte *outBuffer); + void blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer); + void SetCBCMode(bool Mode) {CBCMode=Mode;} +}; + +#endif // _RIJNDAEL_H_ diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rs.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rs.cpp new file mode 100644 index 00000000..10ccc6d7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rs.cpp @@ -0,0 +1,160 @@ +#include "rar.hpp" + +#define Clean(D,S) {for (int I=0;I<(S);I++) (D)[I]=0;} + +void RSCoder::Init(int ParSize) +{ + RSCoder::ParSize=ParSize; // Store the number of recovery volumes. + FirstBlockDone=false; + gfInit(); + pnInit(); +} + + +// Initialize logarithms and exponents Galois field tables. +void RSCoder::gfInit() +{ + for (int I=0,J=1;I<MAXPAR;I++) + { + gfLog[J]=I; + gfExp[I]=J; + J<<=1; + if (J > MAXPAR) + J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1). + } + for (int I=MAXPAR;I<MAXPOL;I++) // Avoid gfExp overflow check. + gfExp[I]=gfExp[I-MAXPAR]; +} + + +// Multiplication over Galois field. +inline int RSCoder::gfMult(int a,int b) +{ + return(a==0 || b == 0 ? 0:gfExp[gfLog[a]+gfLog[b]]); +} + + +// Create the generator polynomial g(x). +// g(x)=(x-a)(x-a^2)(x-a^3)..(x-a^N) +void RSCoder::pnInit() +{ + int p2[MAXPAR+1]; // Currently calculated part of g(x). + + Clean(p2,ParSize); + p2[0]=1; // Set p2 polynomial to 1. + + for (int I=1;I<=ParSize;I++) + { + int p1[MAXPAR+1]; // We use p1 as current (x+a^i) expression. + Clean(p1,ParSize); + p1[0]=gfExp[I]; + p1[1]=1; // Set p1 polynomial to x+a^i. + + // Multiply the already calucated part of g(x) to next (x+a^i). + pnMult(p1,p2,GXPol); + + // p2=g(x). + for (int J=0;J<ParSize;J++) + p2[J]=GXPol[J]; + } +} + + +// Multiply polynomial 'p1' to 'p2' and store the result in 'r'. +void RSCoder::pnMult(int *p1,int *p2,int *r) +{ + Clean(r,ParSize); + for (int I=0;I<ParSize;I++) + if (p1[I]!=0) + for(int J=0;J<ParSize-I;J++) + r[I+J]^=gfMult(p1[I],p2[J]); +} + + +void RSCoder::Encode(byte *Data,int DataSize,byte *DestData) +{ + int ShiftReg[MAXPAR+1]; // Linear Feedback Shift Register. + + Clean(ShiftReg,ParSize+1); + for (int I=0;I<DataSize;I++) + { + int D=Data[I]^ShiftReg[ParSize-1]; + + // Use g(x) to define feedback taps. + for (int J=ParSize-1;J>0;J--) + ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D); + ShiftReg[0]=gfMult(GXPol[0],D); + } + for (int I=0;I<ParSize;I++) + DestData[I]=ShiftReg[ParSize-I-1]; +} + + +bool RSCoder::Decode(byte *Data,int DataSize,int *EraLoc,int EraSize) +{ + int SynData[MAXPOL]; // Syndrome data. + + bool AllZeroes=true; + for (int I=0;I<ParSize;I++) + { + int Sum=0; + for (int J=0;J<DataSize;J++) + Sum=Data[J]^gfMult(gfExp[I+1],Sum); + if ((SynData[I]=Sum)!=0) + AllZeroes=false; + } + + // If all syndrome numbers are zero, message does not have errors. + if (AllZeroes) + return(true); + + if (!FirstBlockDone) // Do things which we need to do once for all data. + { + FirstBlockDone=true; + + // Calculate the error locator polynomial. + Clean(ELPol,ParSize+1); + ELPol[0]=1; + + for (int EraPos=0;EraPos<EraSize;EraPos++) + for (int I=ParSize,M=gfExp[DataSize-EraLoc[EraPos]-1];I>0;I--) + ELPol[I]^=gfMult(M,ELPol[I-1]); + + ErrCount=0; + + // Find roots of error locator polynomial. + for (int Root=MAXPAR-DataSize;Root<MAXPAR+1;Root++) + { + int Sum=0; + for (int B=0;B<ParSize+1;B++) + Sum^=gfMult(gfExp[(B*Root)%MAXPAR],ELPol[B]); + if (Sum==0) // Root found. + { + ErrorLocs[ErrCount]=MAXPAR-Root; // Location of error. + + // Calculate the denominator for every error location. + Dnm[ErrCount]=0; + for (int I=1;I<ParSize+1;I+=2) + Dnm[ErrCount]^= gfMult(ELPol[I],gfExp[Root*(I-1)%MAXPAR]); + + ErrCount++; + } + } + } + + int EEPol[MAXPOL]; // Error Evaluator Polynomial. + pnMult(ELPol,SynData,EEPol); + // If errors are present and their number is correctable. + if ((ErrCount<=ParSize) && ErrCount>0) + for (int I=0;I<ErrCount;I++) + { + int Loc=ErrorLocs[I],DLoc=MAXPAR-Loc,N=0; + for (int J=0;J<ParSize;J++) + N^=gfMult(EEPol[J],gfExp[DLoc*J%MAXPAR]); + int DataPos=DataSize-Loc-1; + // Perform bounds check and correct the data error. + if (DataPos>=0 && DataPos<DataSize) + Data[DataPos]^=gfMult(N,gfExp[MAXPAR-gfLog[Dnm[I]]]); + } + return(ErrCount<=ParSize); // Return true if success. +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rs.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rs.hpp new file mode 100644 index 00000000..6ac80940 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rs.hpp @@ -0,0 +1,32 @@ +#ifndef _RAR_RS_ +#define _RAR_RS_ + +#define MAXPAR 255 // Maximum parity data size. +#define MAXPOL 512 // Maximum polynomial degree. + +class RSCoder +{ + private: + void gfInit(); + int gfMult(int a,int b); + void pnInit(); + void pnMult(int *p1,int *p2,int *r); + + int gfExp[MAXPOL]; // Galois field exponents. + int gfLog[MAXPAR+1]; // Galois field logarithms. + + int GXPol[MAXPOL*2]; // Generator polynomial g(x). + + int ErrorLocs[MAXPAR+1],ErrCount; + int Dnm[MAXPAR+1]; + + int ParSize; // Parity bytes size and so the number of recovery volumes. + int ELPol[MAXPOL]; // Error locator polynomial. + bool FirstBlockDone; + public: + void Init(int ParSize); + void Encode(byte *Data,int DataSize,byte *DestData); + bool Decode(byte *Data,int DataSize,int *EraLoc,int EraSize); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.cpp new file mode 100644 index 00000000..f5c7ccac --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.cpp @@ -0,0 +1,421 @@ +#include "rar.hpp" + +// We used "Screaming Fast Galois Field Arithmetic Using Intel SIMD +// Instructions" paper by James S. Plank, Kevin M. Greenan +// and Ethan L. Miller for fast SSE based multiplication. +// Also we are grateful to Artem Drobanov and Bulat Ziganshin +// for samples and ideas allowed to make Reed-Solomon codec more efficient. + +RSCoder16::RSCoder16() +{ + Decoding=false; + ND=NR=NE=0; + ValidFlags=NULL; + MX=NULL; + DataLog=NULL; + DataLogSize=0; + + gfInit(); +} + + +RSCoder16::~RSCoder16() +{ + delete[] gfExp; + delete[] gfLog; + delete[] DataLog; + delete[] MX; + delete[] ValidFlags; +} + + +// Initialize logarithms and exponents Galois field tables. +void RSCoder16::gfInit() +{ + gfExp=new uint[4*gfSize+1]; + gfLog=new uint[gfSize+1]; + + for (uint L=0,E=1;L<gfSize;L++) + { + gfLog[E]=L; + gfExp[L]=E; + gfExp[L+gfSize]=E; // Duplicate the table to avoid gfExp overflow check. + E<<=1; + if (E>gfSize) + 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; + } + + // 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check. + 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<BlockSize; I+=2) + DataLog[I] = gfLog[ *(ushort*)(Data+I) ]; + else + for (size_t I=0; I<BlockSize; I+=2) + { + uint D=Data[I]+Data[I+1]*256; + DataLog[I] = gfLog[ D ]; + } + } + + uint ML = gfLog[ MX[ECCNum * ND + DataNum] ]; + + if (DirectAccess) + for (size_t I=0; I<BlockSize; I+=2) + *(ushort*)(ECC+I) ^= gfExp[ ML + DataLog[I] ]; + else + for (size_t I=0; I<BlockSize; I+=2) + { + uint R=gfExp[ ML + DataLog[I] ]; + ECC[I]^=byte(R); + ECC[I+1]^=byte(R/256); + } +} + + +#ifdef USE_SSE +// Data and ECC addresses must be properly aligned for SSE. +// AVX2 did not provide a noticeable speed gain on i7-6700K here. +bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize) +{ + // Check data alignment and SSSE3 support. + if ((size_t(Data) & (SSE_ALIGNMENT-1))!=0 || (size_t(ECC) & (SSE_ALIGNMENT-1))!=0 || + _SSE_Version<SSE_SSSE3) + return false; + + uint M=MX[ECCNum * ND + DataNum]; + + // Prepare tables containing products of M and 4, 8, 12, 16 bit length + // numbers, which have 4 high bits in 0..15 range and other bits set to 0. + // Store high and low bytes of resulting 16 bit product in separate tables. + __m128i T0L,T1L,T2L,T3L; // Low byte tables. + __m128i T0H,T1H,T2H,T3H; // High byte tables. + + for (uint I=0;I<16;I++) + { + ((byte *)&T0L)[I]=gfMul(I,M); + ((byte *)&T0H)[I]=gfMul(I,M)>>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 (; Pos<BlockSize; Pos+=2) + *(ushort*)(ECC+Pos) ^= gfMul( M, *(ushort*)(Data+Pos) ); + + return true; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.hpp new file mode 100644 index 00000000..b67a7ca8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/rs16.hpp @@ -0,0 +1,44 @@ +#ifndef _RAR_RS16_ +#define _RAR_RS16_ + +class RSCoder16 +{ + private: + static const uint gfSize=65535; // Galois field size. + void gfInit(); // Galois field inititalization. + inline uint gfAdd(uint a,uint b); // Addition in Galois field. + inline uint gfMul(uint a,uint b); // Multiplication in Galois field. + inline uint gfInv(uint a); // Inverse element in Galois field. + uint *gfExp; // Galois field exponents. + uint *gfLog; // Galois field logarithms. + + void MakeEncoderMatrix(); + void MakeDecoderMatrix(); + void InvertDecoderMatrix(); + +#ifdef USE_SSE + bool SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); +#endif + + bool Decoding; // If we are decoding or encoding data. + uint ND; // Number of data units. + uint NR; // Number of Reed-Solomon code units. + uint NE; // Number of erasures. + bool *ValidFlags; // Validity flags for data and ECC units. + uint *MX; // Cauchy based coding or decoding matrix. + + uint *DataLog; // Buffer to store data logarithms for UpdateECC. + size_t DataLogSize; + + public: + RSCoder16(); + ~RSCoder16(); + + bool Init(uint DataCount, uint RecCount, bool *ValidityFlags); +#if 0 // We use only UpdateECC now. + void Process(const uint *Data, uint *Out); +#endif + void UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/scantree.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/scantree.cpp new file mode 100644 index 00000000..a13a3ebc --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/scantree.cpp @@ -0,0 +1,494 @@ +#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; + uint StartPos=0; +#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\') + StartPos=4; +#endif + for (uint I=StartPos;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. + wcsncpyz(Filter,L"*",ASIZE(Filter)); + 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; + + wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); + + 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]; + wcsncpyz(Mask,Slash,ASIZE(Mask)); + if (Depth<SetAllMaskDepth) + wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1); + *Slash=0; + wcsncpyz(DirName,CurMask,ASIZE(DirName)); + wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER); + if (PrevSlash==NULL) + wcsncpyz(CurMask,Mask+1,ASIZE(CurMask)); + else + { + *PrevSlash=0; + wcsncatz(CurMask,Mask,ASIZE(CurMask)); + } + } + if (GetDirs==SCAN_GETDIRSTWICE && + FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir) + { + 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]; + + wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); + wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); + + 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/Src/external_dependencies/openmpt-trunk/include/unrar/scantree.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/scantree.hpp new file mode 100644 index 00000000..7ebe69ad --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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<uint> *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<uint> *Lengths) + { + ErrDirList=List; + ErrDirSpecPathLength=Lengths; + } +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.cpp new file mode 100644 index 00000000..2b9f8378 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.cpp @@ -0,0 +1,229 @@ +#include "rar.hpp" + +/* // OPENMPT ADDITION +#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. +static CryptLoader GlobalCryptLoader; +#endif +*/ // OPENMPT ADDITION + +SecPassword::SecPassword() +{ + return; // OPENMPT ADDITION + CrossProcess=false; + Set(L""); +} + + +SecPassword::~SecPassword() +{ + return; // OPENMPT ADDITION + Clean(); +} + + +void SecPassword::Clean() +{ + return; // OPENMPT ADDITION + 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) +{ + return; // OPENMPT ADDITION + 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<size;i++) + d[i]=0; +#endif +} + + +// We got a complain from user that it is possible to create WinRAR dump +// with "Create dump file" command in Windows Task Manager and then easily +// locate Unicode password string in the dump. It is unsecure if several +// people share the same computer and somebody left WinRAR copy with entered +// password. So we decided to obfuscate the password to make it more difficult +// to find it in dump. +void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode) +{ + return; // OPENMPT ADDITION + // Source string can be shorter than destination as in case when we process + // -p<pwd> 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) +{ + return; // OPENMPT ADDITION + if (PasswordSet) + { + Process(Password,ASIZE(Password),Psw,MaxSize,false); + Psw[MaxSize-1]=0; + } + else + *Psw=0; +} + + + + +void SecPassword::Set(const wchar *Psw) +{ + return; // OPENMPT ADDITION + 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() +{ + return 0; // OPENMPT ADDITION + wchar Plain[MAXPASSWORD]; + Get(Plain,ASIZE(Plain)); + size_t Length=wcslen(Plain); + cleandata(Plain,ASIZE(Plain)); + return Length; +} + + +bool SecPassword::operator == (SecPassword &psw) +{ + return false; // OPENMPT ADDITION + // 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) +{ + /* // OPENMPT ADDITION + // 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<DataSize;I++) + *((byte *)Data+I)^=Key+I+75; + */ // OPENMPT ADDITION +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.hpp new file mode 100644 index 00000000..375d3887 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/secpassword.hpp @@ -0,0 +1,35 @@ +#ifndef _RAR_SECURE_PASSWORD_ +#define _RAR_SECURE_PASSWORD_ + +// Store a password securely (if data encryption is provided by OS) +// or obfuscated to make search for password in memory dump less trivial. +class SecPassword +{ + private: + void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); + + wchar Password[MAXPASSWORD]; + + // It is important to have this 'bool' value, so if our object is cleaned + // with memset as a part of larger structure, it is handled correctly. + bool PasswordSet; + public: + SecPassword(); + ~SecPassword(); + void Clean(); + void Get(wchar *Psw,size_t MaxSize); + void Set(const wchar *Psw); + bool IsSet() {return PasswordSet;} + size_t Length(); + bool operator == (SecPassword &psw); + + // Set to true if we need to pass a password to another process. + // We use it when transferring parameters to UAC elevated WinRAR. + bool CrossProcess; +}; + + +void cleandata(void *data,size_t size); +void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/sha1.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/sha1.cpp new file mode 100644 index 00000000..562aadd0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/sha1.cpp @@ -0,0 +1,204 @@ +#include "rar.hpp" + +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +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/Src/external_dependencies/openmpt-trunk/include/unrar/sha1.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/sha1.hpp new file mode 100644 index 00000000..7c0b7fb7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/sha256.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/sha256.cpp new file mode 100644 index 00000000..f90d2c09 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/sha256.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/sha256.hpp new file mode 100644 index 00000000..b6837e76 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/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/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.cpp new file mode 100644 index 00000000..81259d02 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.cpp @@ -0,0 +1,19 @@ +#include "rar.hpp" + +int ToPercent(int64 N1,int64 N2) +{ + if (N2<N1) + return 100; + return ToPercentUnlim(N1,N2); +} + + +// Allows the percent larger than 100. +int ToPercentUnlim(int64 N1,int64 N2) +{ + if (N2==0) + return 0; + return (int)(N1*100/N2); +} + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.hpp new file mode 100644 index 00000000..f53daa8b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/smallfn.hpp @@ -0,0 +1,8 @@ +#ifndef _RAR_SMALLFN_ +#define _RAR_SMALLFN_ + +int ToPercent(int64 N1,int64 N2); +int ToPercentUnlim(int64 N1,int64 N2); +void RARInitData(); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.cpp new file mode 100644 index 00000000..f53c826a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.cpp @@ -0,0 +1,444 @@ +#include "rar.hpp" + +const char *NullToEmpty(const char *Str) +{ + return Str==NULL ? "":Str; +} + + +const wchar *NullToEmpty(const wchar *Str) +{ + return Str==NULL ? L"":Str; +} + + +void IntToExt(const char *Src,char *Dest,size_t DestSize) +{ +#ifdef _WIN_ALL + // OemToCharBuff does not stop at 0, so let's check source length. + size_t SrcLength=strlen(Src)+1; + if (DestSize>SrcLength) + DestSize=SrcLength; + OemToCharBuffA(Src,Dest,(DWORD)DestSize); + Dest[DestSize-1]=0; +#else + if (Dest!=Src) + strncpyz(Dest,Src,DestSize); +#endif +} + + +// Convert archived names and comments to Unicode. +// Allows 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 + { + Array<char> NameA; + if (Encoding==ACTW_OEM) + { + NameA.Alloc(DestSize+1); + IntToExt(Src,&NameA[0],NameA.Size()); + Src=&NameA[0]; + } + 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; +} + + +#if defined(SFX_MODULE) +// char version of etoupperw. Used in console SFX module only. +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all +// signed chars passed to this function to unsigned char. +unsigned char etoupper(unsigned char c) +{ + return c>='a' && c<='z' ? c-'a'+'A' : c; +} +#endif + + +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all +// signed wchars passed to this function to unsigned char. +wchar etoupperw(wchar c) +{ + return c>='a' && c<='z' ? c-'a'+'A' : c; +} + + +// 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<BinSize;I++) + { + uint High=Bin[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 && A<HexSize-2) // Need space for 2 chars and final zero. + { + HexA[A++]=(char)HighHex; + HexA[A++]=(char)LowHex; + } + if (HexW!=NULL && W<HexSize-2) // Need space for 2 chars and final zero. + { + HexW[W++]=HighHex; + HexW[W++]=LowHex; + } + } + if (HexA!=NULL && HexSize>0) + 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 (size_t 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 (size_t 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 copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void strncpyz(char *dest, const char *src, size_t maxlen) +{ + if (maxlen>0) + { + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; + } +} + + +// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen) +{ + if (maxlen>0) + { + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; + } +} + + +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void strncatz(char* dest, const char* src, size_t maxlen) +{ + size_t length = strlen(dest); + if (maxlen > length) + strncpyz(dest + length, src, maxlen - length); +} + + +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void wcsncatz(wchar* dest, const wchar* src, size_t maxlen) +{ + size_t length = wcslen(dest); + if (maxlen > length) + wcsncpyz(dest + length, src, maxlen - length); +} + + +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<Pos;I++) + Str[I]=NumStr[Pos-I-1]; + Str[Pos]=0; +} + + +void itoa(int64 n,wchar *Str,size_t MaxSize) +{ + wchar 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++]=wchar(n%10)+'0'; + n=n/10; + } while (n!=0); + + if (Neg) + NumStr[Pos++]='-'; + + for (size_t I=0;I<Pos;I++) + Str[I]=NumStr[Pos-I-1]; + Str[Pos]=0; +} + + +const wchar* GetWide(const char *Src) +{ + const size_t MaxLength=NM; + static wchar StrTable[4][MaxLength]; + static uint StrNum=0; + if (++StrNum >= ASIZE(StrTable)) + StrNum=0; + wchar *Str=StrTable[StrNum]; + CharToWide(Src,Str,MaxLength); + Str[MaxLength-1]=0; + return Str; +} + + +// Parse string containing parameters separated with spaces. +// Support quote marks. Param can be NULL to return the pointer to next +// parameter, which can be used to estimate the buffer size for Param. +const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize) +{ + while (IsSpace(*CmdLine)) + CmdLine++; + if (*CmdLine==0) + return NULL; + + size_t ParamSize=0; + bool Quote=false; + while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine))) + { + if (*CmdLine=='\"') + { + if (CmdLine[1]=='\"') + { + // Insert the quote character instead of two adjoining quote characters. + if (Param!=NULL && ParamSize<MaxSize-1) + Param[ParamSize++]='\"'; + CmdLine++; + } + else + Quote=!Quote; + } + else + if (Param!=NULL && ParamSize<MaxSize-1) + Param[ParamSize++]=*CmdLine; + CmdLine++; + } + if (Param!=NULL) + Param[ParamSize]=0; + return CmdLine; +} + + +#ifndef RARDLL +// For compatibility with existing translations we use %s to print Unicode +// strings in format strings and convert them to %ls here. %s could work +// without such conversion in Windows, but not in Unix wprintf. +void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize) +{ + uint Src=0,Dest=0; + while (Org[Src]!=0 && Dest<MaxSize-1) + { + if (Org[Src]=='%' && (Src==0 || Org[Src-1]!='%')) + { + uint SPos=Src+1; + // Skipping a possible width specifier like %-50s. + while (IsDigit(Org[SPos]) || Org[SPos]=='-') + SPos++; + if (Org[SPos]=='s' && Dest<MaxSize-(SPos-Src+1)) + { + while (Src<SPos) + Cvt[Dest++]=Org[Src++]; + Cvt[Dest++]='l'; + } + } +#ifdef _WIN_ALL + // Convert \n to \r\n in Windows. Important when writing to log, + // so other tools like Notebook can view resulting log properly. + if (Org[Src]=='\n' && (Src==0 || Org[Src-1]!='\r')) + Cvt[Dest++]='\r'; +#endif + + Cvt[Dest++]=Org[Src++]; + } + Cvt[Dest]=0; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.hpp new file mode 100644 index 00000000..32470310 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/strfn.hpp @@ -0,0 +1,51 @@ +#ifndef _RAR_STRFN_ +#define _RAR_STRFN_ + +const char* NullToEmpty(const char *Str); +const wchar* NullToEmpty(const wchar *Str); +void IntToExt(const char *Src,char *Dest,size_t DestSize); + +enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8}; +void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding); + + +int stricomp(const char *s1,const char *s2); +int strnicomp(const char *s1,const char *s2,size_t n); +wchar* RemoveEOL(wchar *Str); +wchar* RemoveLF(wchar *Str); + +void strncpyz(char *dest, const char *src, size_t maxlen); +void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen); +void strncatz(char* dest, const char* src, size_t maxlen); +void wcsncatz(wchar* dest, const wchar* src, size_t maxlen); + +#if defined(SFX_MODULE) +unsigned char etoupper(unsigned char c); +#endif +wchar etoupperw(wchar c); + +bool IsDigit(int ch); +bool IsSpace(int ch); +bool IsAlpha(int ch); + +void BinToHex(const byte *Bin,size_t BinSize,char *Hex,wchar *HexW,size_t HexSize); + +#ifndef SFX_MODULE +uint GetDigits(uint Number); +#endif + +bool LowAscii(const char *Str); +bool LowAscii(const wchar *Str); + +int wcsicompc(const wchar *s1,const wchar *s2); +int wcsnicompc(const wchar *s1,const wchar *s2,size_t n); + +void itoa(int64 n,char *Str,size_t MaxSize); +void itoa(int64 n,wchar *Str,size_t MaxSize); +const wchar* GetWide(const char *Src); +const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize); +#ifndef RARDLL +void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize); +#endif + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.cpp new file mode 100644 index 00000000..50d69c71 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.cpp @@ -0,0 +1,151 @@ +#include "rar.hpp" + +StringList::StringList() +{ + Reset(); +} + + +void StringList::Reset() +{ + Rewind(); + StringData.Reset(); + StringsCount=0; + SavePosNumber=0; +} + + +void StringList::AddStringA(const char *Str) +{ + Array<wchar> StrW(strlen(Str)); + CharToWide(Str,&StrW[0],StrW.Size()); + AddString(&StrW[0]); +} + + +void StringList::AddString(const wchar *Str) +{ + if (Str==NULL) + Str=L""; + + size_t PrevSize=StringData.Size(); + StringData.Add(wcslen(Str)+1); + wcscpy(&StringData[PrevSize],Str); + + StringsCount++; +} + + +bool StringList::GetStringA(char *Str,size_t MaxLength) +{ + Array<wchar> StrW(MaxLength); + if (!GetString(&StrW[0],StrW.Size())) + return false; + WideToChar(&StrW[0],Str,MaxLength); + return true; +} + + +bool StringList::GetString(wchar *Str,size_t MaxLength) +{ + wchar *StrPtr; + if (!GetString(&StrPtr)) + return false; + wcsncpyz(Str,StrPtr,MaxLength); + return true; +} + + +#ifndef SFX_MODULE +bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) +{ + SavePosition(); + Rewind(); + bool RetCode=true; + while (StringNum-- >=0) + if (!GetString(Str,MaxLength)) + { + RetCode=false; + break; + } + RestorePosition(); + return RetCode; +} +#endif + + +wchar* StringList::GetString() +{ + wchar *Str; + GetString(&Str); + return Str; +} + + +bool StringList::GetString(wchar **Str) +{ + if (CurPos>=StringData.Size()) // No more strings left unprocessed. + { + if (Str!=NULL) + *Str=NULL; + return false; + } + + wchar *CurStr=&StringData[CurPos]; + CurPos+=wcslen(CurStr)+1; + if (Str!=NULL) + *Str=CurStr; + + return true; +} + + +void StringList::Rewind() +{ + CurPos=0; +} + + +#ifndef SFX_MODULE +bool StringList::Search(const wchar *Str,bool CaseSensitive) +{ + SavePosition(); + Rewind(); + bool Found=false; + wchar *CurStr; + while (GetString(&CurStr)) + { + if (Str!=NULL && CurStr!=NULL) + if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0) + continue; + Found=true; + break; + } + RestorePosition(); + return Found; +} +#endif + + +#ifndef SFX_MODULE +void StringList::SavePosition() +{ + if (SavePosNumber<ASIZE(SaveCurPos)) + { + SaveCurPos[SavePosNumber]=CurPos; + SavePosNumber++; + } +} +#endif + + +#ifndef SFX_MODULE +void StringList::RestorePosition() +{ + if (SavePosNumber>0) + { + SavePosNumber--; + CurPos=SaveCurPos[SavePosNumber]; + } +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.hpp new file mode 100644 index 00000000..16a2cbb0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/strlist.hpp @@ -0,0 +1,31 @@ +#ifndef _RAR_STRLIST_ +#define _RAR_STRLIST_ + +class StringList +{ + private: + Array<wchar> StringData; + size_t CurPos; + + size_t StringsCount; + + size_t SaveCurPos[16],SavePosNumber; + public: + StringList(); + void Reset(); + void AddStringA(const char *Str); + void AddString(const wchar *Str); + bool GetStringA(char *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength,int StringNum); + wchar* GetString(); + bool GetString(wchar **Str); + void Rewind(); + size_t ItemsCount() {return StringsCount;}; + size_t GetCharCount() {return StringData.Size();} + bool Search(const wchar *Str,bool CaseSensitive); + void SavePosition(); + void RestorePosition(); +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.cpp new file mode 100644 index 00000000..07d32859 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** + * This file is part of PPMd project * + * Written and distributed to public domain by Dmitry Shkarin 1997, * + * 1999-2000 * + * Contents: memory allocation routines * + ****************************************************************************/ + +static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK)); +static const uint FIXED_UNIT_SIZE=12; + +SubAllocator::SubAllocator() +{ + Clean(); +} + + +void SubAllocator::Clean() +{ + SubAllocatorSize=0; +} + + +inline void SubAllocator::InsertNode(void* p,int indx) +{ + ((RAR_NODE*) p)->next=FreeList[indx].next; + FreeList[indx].next=(RAR_NODE*) p; +} + + +inline void* SubAllocator::RemoveNode(int indx) +{ + RAR_NODE* RetVal=FreeList[indx].next; + FreeList[indx].next=RetVal->next; + return RetVal; +} + + +inline uint SubAllocator::U2B(int NU) +{ + // We calculate the size of units in bytes based on real UNIT_SIZE. + // In original implementation it was 8*NU+4*NU. + return UNIT_SIZE*NU; +} + + + +// Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be +// equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address. +inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items) +{ + return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); +} + + +inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx) +{ + int i, UDiff=Indx2Units[OldIndx]-Indx2Units[NewIndx]; + byte* p=((byte*) pv)+U2B(Indx2Units[NewIndx]); + if (Indx2Units[i=Units2Indx[UDiff-1]] != UDiff) + { + InsertNode(p,--i); + p += U2B(i=Indx2Units[i]); + UDiff -= i; + } + InsertNode(p,Units2Indx[UDiff-1]); +} + + +void SubAllocator::StopSubAllocator() +{ + if ( SubAllocatorSize ) + { + SubAllocatorSize=0; + free(HeapStart); + } +} + + +bool SubAllocator::StartSubAllocator(int SASize) +{ + uint t=SASize << 20; + if (SubAllocatorSize == t) + return true; + StopSubAllocator(); + + // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size + // can be larger. So let's recalculate the allocated size and add two more + // units: one as reserve for HeapEnd overflow checks and another + // to provide the space to correctly align UnitsStart. + uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE; + if ((HeapStart=(byte *)malloc(AllocSize)) == NULL) + { + ErrHandler.MemoryError(); + return false; + } + + // HeapEnd did not present in original algorithm. We added it to control + // invalid memory access attempts when processing corrupt archived data. + HeapEnd=HeapStart+AllocSize-UNIT_SIZE; + + SubAllocatorSize=t; + return true; +} + + +void SubAllocator::InitSubAllocator() +{ + int i, k; + memset(FreeList,0,sizeof(FreeList)); + pText=HeapStart; + + // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual + // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value + // because of alignment and larger pointer fields size. + // So we define UNIT_SIZE for this larger size and adjust memory + // pointers accordingly. + + // Size2 is (HiUnit-LoUnit) memory area size to allocate as originally + // supposed by compression algorithm. It is 7/8 of total allocated size. + uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7); + + // RealSize2 is the real adjusted size of (HiUnit-LoUnit) memory taking + // into account that our UNIT_SIZE can be larger than FIXED_UNIT_SIZE. + uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE; + + // Size1 is the size of memory area from HeapStart to FakeUnitsStart + // as originally supposed by compression algorithm. This area can contain + // different data types, both single symbols and structures. + uint Size1=SubAllocatorSize-Size2; + + // Real size of this area. We correct it according to UNIT_SIZE vs + // FIXED_UNIT_SIZE difference. Also we add one more UNIT_SIZE + // to compensate a possible reminder from Size1/FIXED_UNIT_SIZE, + // which would be lost otherwise. We add UNIT_SIZE instead of + // this Size1%FIXED_UNIT_SIZE reminder, because it allows to align + // UnitsStart easily and adding more than reminder is ok for algorithm. + uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE; + + // RealSize1 must be divided by UNIT_SIZE without a reminder, so UnitsStart + // is aligned to UNIT_SIZE. It is important for those architectures, + // where a proper memory alignment is mandatory. Since we produce RealSize1 + // multiplying by UNIT_SIZE, this condition is always true. So LoUnit, + // UnitsStart, HeapStart are properly aligned, + LoUnit=UnitsStart=HeapStart+RealSize1; + + // When we reach FakeUnitsStart, we restart the model. It is where + // the original algorithm expected to see UnitsStart. Real UnitsStart + // can have a larger value. + FakeUnitsStart=HeapStart+Size1; + + HiUnit=LoUnit+RealSize2; + for (i=0,k=1;i < N1 ;i++,k += 1) + Indx2Units[i]=k; + for (k++;i < N1+N2 ;i++,k += 2) + Indx2Units[i]=k; + for (k++;i < N1+N2+N3 ;i++,k += 3) + Indx2Units[i]=k; + for (k++;i < N1+N2+N3+N4;i++,k += 4) + Indx2Units[i]=k; + for (GlueCount=k=i=0;k < 128;k++) + { + i += (Indx2Units[i] < k+1); + Units2Indx[k]=i; + } +} + + +inline void SubAllocator::GlueFreeBlocks() +{ + RARPPM_MEM_BLK s0, * p, * p1; + int i, k, sz; + if (LoUnit != HiUnit) + *LoUnit=0; + for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++) + while ( FreeList[i].next ) + { + p=(RARPPM_MEM_BLK*)RemoveNode(i); + p->insertAt(&s0); + p->Stamp=0xFFFF; + p->NU=Indx2Units[i]; + } + for (p=s0.next;p != &s0;p=p->next) + while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU < 0x10000) + { + p1->remove(); + p->NU += p1->NU; + } + while ((p=s0.next) != &s0) + { + for (p->remove(), sz=p->NU;sz > 128;sz -= 128, p=MBPtr(p,128)) + InsertNode(p,N_INDEXES-1); + if (Indx2Units[i=Units2Indx[sz-1]] != sz) + { + k=sz-Indx2Units[--i]; + InsertNode(MBPtr(p,sz-k),k-1); + } + InsertNode(p,i); + } +} + +void* SubAllocator::AllocUnitsRare(int indx) +{ + if ( !GlueCount ) + { + GlueCount = 255; + GlueFreeBlocks(); + if ( FreeList[indx].next ) + return RemoveNode(indx); + } + int i=indx; + do + { + if (++i == N_INDEXES) + { + GlueCount--; + i=U2B(Indx2Units[indx]); + int j=FIXED_UNIT_SIZE*Indx2Units[indx]; + if (FakeUnitsStart - pText > j) + { + FakeUnitsStart -= j; + UnitsStart -= i; + return UnitsStart; + } + return NULL; + } + } while ( !FreeList[i].next ); + void* RetVal=RemoveNode(i); + SplitBlock(RetVal,i,indx); + return RetVal; +} + + +inline void* SubAllocator::AllocUnits(int NU) +{ + int indx=Units2Indx[NU-1]; + if ( FreeList[indx].next ) + return RemoveNode(indx); + void* RetVal=LoUnit; + LoUnit += U2B(Indx2Units[indx]); + if (LoUnit <= HiUnit) + return RetVal; + LoUnit -= U2B(Indx2Units[indx]); + return AllocUnitsRare(indx); +} + + +void* SubAllocator::AllocContext() +{ + if (HiUnit != LoUnit) + return (HiUnit -= UNIT_SIZE); + if ( FreeList->next ) + return RemoveNode(0); + return AllocUnitsRare(0); +} + + +void* SubAllocator::ExpandUnits(void* OldPtr,int OldNU) +{ + int i0=Units2Indx[OldNU-1], i1=Units2Indx[OldNU-1+1]; + if (i0 == i1) + return OldPtr; + void* ptr=AllocUnits(OldNU+1); + if ( ptr ) + { + memcpy(ptr,OldPtr,U2B(OldNU)); + InsertNode(OldPtr,i0); + } + return ptr; +} + + +void* SubAllocator::ShrinkUnits(void* OldPtr,int OldNU,int NewNU) +{ + int i0=Units2Indx[OldNU-1], i1=Units2Indx[NewNU-1]; + if (i0 == i1) + return OldPtr; + if ( FreeList[i1].next ) + { + void* ptr=RemoveNode(i1); + memcpy(ptr,OldPtr,U2B(NewNU)); + InsertNode(OldPtr,i0); + return ptr; + } + else + { + SplitBlock(OldPtr,i0,i1); + return OldPtr; + } +} + + +void SubAllocator::FreeUnits(void* ptr,int OldNU) +{ + InsertNode(ptr,Units2Indx[OldNU-1]); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.hpp new file mode 100644 index 00000000..2a1d1320 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/suballoc.hpp @@ -0,0 +1,86 @@ +/**************************************************************************** + * This file is part of PPMd project * + * Written and distributed to public domain by Dmitry Shkarin 1997, * + * 1999-2000 * + * Contents: interface to memory allocation routines * + ****************************************************************************/ +#if !defined(_SUBALLOC_H_) +#define _SUBALLOC_H_ + +#if defined(__GNUC__) && defined(ALLOW_MISALIGNED) +#define RARPPM_PACK_ATTR __attribute__ ((packed)) +#else +#define RARPPM_PACK_ATTR +#endif /* defined(__GNUC__) */ + +#ifdef ALLOW_MISALIGNED +#pragma pack(1) +#endif + +struct RARPPM_MEM_BLK +{ + ushort Stamp, NU; + RARPPM_MEM_BLK* next, * prev; + void insertAt(RARPPM_MEM_BLK* p) + { + next=(prev=p)->next; + p->next=next->prev=this; + } + void remove() + { + prev->next=next; + next->prev=prev; + } +} RARPPM_PACK_ATTR; + +#ifdef ALLOW_MISALIGNED +#ifdef _AIX +#pragma pack(pop) +#else +#pragma pack() +#endif +#endif + + +class SubAllocator +{ + private: + static const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; + static const int N_INDEXES=N1+N2+N3+N4; + + struct RAR_NODE + { + RAR_NODE* next; + }; + + inline void InsertNode(void* p,int indx); + inline void* RemoveNode(int indx); + inline uint U2B(int NU); + inline void SplitBlock(void* pv,int OldIndx,int NewIndx); + inline void GlueFreeBlocks(); + void* AllocUnitsRare(int indx); + inline RARPPM_MEM_BLK* MBPtr(RARPPM_MEM_BLK *BasePtr,int Items); + + long SubAllocatorSize; + byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; + byte *HeapStart,*LoUnit, *HiUnit; + struct RAR_NODE FreeList[N_INDEXES]; + public: + SubAllocator(); + ~SubAllocator() {StopSubAllocator();} + void Clean(); + bool StartSubAllocator(int SASize); + void StopSubAllocator(); + void InitSubAllocator(); + inline void* AllocContext(); + inline void* AllocUnits(int NU); + inline void* ExpandUnits(void* ptr,int OldNU); + inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU); + inline void FreeUnits(void* ptr,int OldNU); + long GetAllocatedMemory() {return(SubAllocatorSize);} + + byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; +}; + + +#endif /* !defined(_SUBALLOC_H_) */ diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/system.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/system.cpp new file mode 100644 index 00000000..1825e89a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/system.cpp @@ -0,0 +1,217 @@ +#include "rar.hpp" + +static int SleepTime=0; + +void InitSystemOptions(int SleepTime) +{ + ::SleepTime=SleepTime; +} + + +#if !defined(SFX_MODULE) +void SetPriority(int Priority) +{ +#ifdef _WIN_ALL + uint PriorityClass; + int PriorityLevel; + if (Priority<1 || Priority>15) + return; + + if (Priority==1) + { + PriorityClass=IDLE_PRIORITY_CLASS; + PriorityLevel=THREAD_PRIORITY_IDLE; + +// Background mode for Vista, can be slow for many small files. +// if (WinNT()>=WNT_VISTA) +// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); + } + else + if (Priority<7) + { + PriorityClass=IDLE_PRIORITY_CLASS; + PriorityLevel=Priority-4; + } + else + if (Priority==7) + { + PriorityClass=BELOW_NORMAL_PRIORITY_CLASS; + PriorityLevel=THREAD_PRIORITY_ABOVE_NORMAL; + } + else + if (Priority<10) + { + PriorityClass=NORMAL_PRIORITY_CLASS; + PriorityLevel=Priority-7; + } + else + if (Priority==10) + { + PriorityClass=ABOVE_NORMAL_PRIORITY_CLASS; + PriorityLevel=THREAD_PRIORITY_NORMAL; + } + else + { + PriorityClass=HIGH_PRIORITY_CLASS; + PriorityLevel=Priority-13; + } + SetPriorityClass(GetCurrentProcess(),PriorityClass); + SetThreadPriority(GetCurrentThread(),PriorityLevel); + +#ifdef RAR_SMP + ThreadPool::SetPriority(PriorityLevel); +#endif + +#endif +} +#endif + + +// Monotonic clock. Like clock(), returns time passed in CLOCKS_PER_SEC items. +// In Android 5+ and Unix usual clock() returns time spent by all threads +// together, so we cannot use it to measure time intervals anymore. +clock_t MonoClock() +{ + return clock(); +} + + + +void Wait() +{ + return; // OPENMPT ADDITION + if (ErrHandler.UserBreak) + ErrHandler.Exit(RARX_USERBREAK); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (SleepTime!=0) + { + static clock_t LastTime=MonoClock(); + if (MonoClock()-LastTime>10*CLOCKS_PER_SEC/1000) + { + Sleep(SleepTime); + LastTime=MonoClock(); + } + } +#endif +#if defined(_WIN_ALL) + // Reset system sleep timer to prevent system going sleep. + SetThreadExecutionState(ES_SYSTEM_REQUIRED); +#endif +} + + + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void Shutdown(POWER_MODE Mode) +{ + return; // OPENMPT ADDITION + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) + { + LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid); + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0); + } + if (Mode==POWERMODE_OFF) + ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); + if (Mode==POWERMODE_SLEEP) + SetSuspendState(FALSE,FALSE,FALSE); + if (Mode==POWERMODE_HIBERNATE) + SetSuspendState(TRUE,FALSE,FALSE); + if (Mode==POWERMODE_RESTART) + ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); +} + + +bool ShutdownCheckAnother(bool Open) +{ + const wchar *EventName=L"rar -ioff"; + static HANDLE hEvent=NULL; + bool Result=false; // Return false if no other RAR -ioff are running. + if (Open) // Create or open the event. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + else + { + if (hEvent!=NULL) + CloseHandle(hEvent); // Close our event. + // Check if other copies still own the event. While race conditions + // are possible, they are improbable and their harm is minimal. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + Result=GetLastError()==ERROR_ALREADY_EXISTS; + if (hEvent!=NULL) + CloseHandle(hEvent); + } + return Result; +} +#endif + + + + +#if defined(_WIN_ALL) +// Load library from Windows System32 folder. Use this function to prevent +// loading a malicious code from current folder or same folder as exe. +HMODULE WINAPI LoadSysLibrary(const wchar *Name) +{ + wchar SysDir[NM]; + if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0) + return NULL; + MakeName(SysDir,Name,SysDir,ASIZE(SysDir)); + return LoadLibrary(SysDir); +} + + +bool IsUserAdmin() +{ + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup; + BOOL b = AllocateAndInitializeSid(&NtAuthority,2,SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (b) + { + if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) + b = FALSE; + FreeSid(AdministratorsGroup); + } + return b!=FALSE; +} + +#endif + + +#ifdef USE_SSE +SSE_VERSION _SSE_Version=GetSSEVersion(); + +SSE_VERSION GetSSEVersion() +{ + int CPUInfo[4]; + __cpuid(CPUInfo, 0x80000000); + + // Maximum supported cpuid function. For example, Pentium M 755 returns 4 here. + uint MaxSupported=CPUInfo[0] & 0x7fffffff; + + if (MaxSupported>=7) + { + __cpuid(CPUInfo, 7); + if ((CPUInfo[1] & 0x20)!=0) + return SSE_AVX2; + } + if (MaxSupported>=1) + { + __cpuid(CPUInfo, 1); + if ((CPUInfo[2] & 0x80000)!=0) + return SSE_SSE41; + if ((CPUInfo[2] & 0x200)!=0) + return SSE_SSSE3; + if ((CPUInfo[3] & 0x4000000)!=0) + return SSE_SSE2; + if ((CPUInfo[3] & 0x2000000)!=0) + return SSE_SSE; + } + return SSE_NONE; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/system.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/system.hpp new file mode 100644 index 00000000..a56d6b7f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/system.hpp @@ -0,0 +1,40 @@ +#ifndef _RAR_SYSTEM_ +#define _RAR_SYSTEM_ + +#ifdef _WIN_ALL +#ifndef BELOW_NORMAL_PRIORITY_CLASS +#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 +#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 +#endif +#ifndef PROCESS_MODE_BACKGROUND_BEGIN +#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 +#define PROCESS_MODE_BACKGROUND_END 0x00200000 +#endif +#ifndef SHTDN_REASON_MAJOR_APPLICATION +#define SHTDN_REASON_MAJOR_APPLICATION 0x00040000 +#define SHTDN_REASON_FLAG_PLANNED 0x80000000 +#define SHTDN_REASON_MINOR_MAINTENANCE 0x00000001 +#endif +#endif + +void InitSystemOptions(int SleepTime); +void SetPriority(int Priority); +clock_t MonoClock(); +void Wait(); +bool EmailFile(const wchar *FileName,const wchar *MailToW); +void Shutdown(POWER_MODE Mode); +bool ShutdownCheckAnother(bool Open); + +#ifdef _WIN_ALL +HMODULE WINAPI LoadSysLibrary(const wchar *Name); +bool IsUserAdmin(); +#endif + + +#ifdef USE_SSE +enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2}; +SSE_VERSION GetSSEVersion(); +extern SSE_VERSION _SSE_Version; +#endif + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/threadmisc.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/threadmisc.cpp new file mode 100644 index 00000000..742eda41 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/threadmisc.cpp @@ -0,0 +1,151 @@ +static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + InitializeCriticalSection(CritSection); + return true; +#elif defined(_UNIX) + return pthread_mutex_init(CritSection,NULL)==0; +#endif +} + + +static inline void CriticalSectionDelete(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + DeleteCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_destroy(CritSection); +#endif +} + + +static inline void CriticalSectionStart(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + EnterCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_lock(CritSection); +#endif +} + + +static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + LeaveCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_unlock(CritSection); +#endif +} + + +static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data) +{ +#ifdef _UNIX +/* + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); +*/ + pthread_t pt; + int Code=pthread_create(&pt,NULL/*&attr*/,Proc,Data); + if (Code!=0) + { + wchar Msg[100]; + swprintf(Msg,ASIZE(Msg),L"\npthread_create failed, code %d\n",Code); + ErrHandler.GeneralErrMsg(Msg); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return pt; +#else + DWORD ThreadId; + HANDLE hThread=CreateThread(NULL,0x10000,Proc,Data,0,&ThreadId); + if (hThread==NULL) + { + ErrHandler.GeneralErrMsg(L"CreateThread failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return hThread; +#endif +} + + +static void ThreadClose(THREAD_HANDLE hThread) +{ +#ifdef _UNIX + pthread_join(hThread,NULL); +#else + CloseHandle(hThread); +#endif +} + + +#ifdef _WIN_ALL +static void CWaitForSingleObject(HANDLE hHandle) +{ + DWORD rc=WaitForSingleObject(hHandle,INFINITE); + if (rc==WAIT_FAILED) + { + ErrHandler.GeneralErrMsg(L"\nWaitForMultipleObjects error %d, GetLastError %d",rc,GetLastError()); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +#ifdef _UNIX +static void cpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + int rc=pthread_cond_wait(cond,mutex); + if (rc!=0) + { + ErrHandler.GeneralErrMsg(L"\npthread_cond_wait error %d",rc); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +uint GetNumberOfCPU() +{ +#ifndef RAR_SMP + return 1; +#else +#ifdef _UNIX +#ifdef _SC_NPROCESSORS_ONLN + uint Count=(uint)sysconf(_SC_NPROCESSORS_ONLN); + return Count<1 ? 1:Count; +#elif defined(_APPLE) + uint Count; + size_t Size=sizeof(Count); + return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1; +#endif +#else // !_UNIX + DWORD_PTR ProcessMask; + DWORD_PTR SystemMask; + + if (!GetProcessAffinityMask(GetCurrentProcess(),&ProcessMask,&SystemMask)) + return 1; + uint Count=0; + for (DWORD_PTR Mask=1;Mask!=0;Mask<<=1) + if ((ProcessMask & Mask)!=0) + Count++; + return Count<1 ? 1:Count; +#endif + +#endif // RAR_SMP +} + + +uint GetNumberOfThreads() +{ + uint NumCPU=GetNumberOfCPU(); + if (NumCPU<1) + return 1; + if (NumCPU>MaxPoolThreads) + return MaxPoolThreads; + return NumCPU; +} + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.cpp new file mode 100644 index 00000000..8c63a8bd --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.cpp @@ -0,0 +1,212 @@ +#include "rar.hpp" + +#ifdef RAR_SMP +#include "threadmisc.cpp" + +#ifdef _WIN_ALL +int ThreadPool::ThreadPriority=THREAD_PRIORITY_NORMAL; +#endif + +ThreadPool::ThreadPool(uint MaxThreads) +{ + MaxAllowedThreads = MaxThreads; + if (MaxAllowedThreads>MaxPoolThreads) + MaxAllowedThreads=MaxPoolThreads; + if (MaxAllowedThreads==0) + MaxAllowedThreads=1; + + ThreadsCreatedCount=0; + + // If we have more threads than queue size, we'll hang on pool destroying, + // not releasing all waiting threads. + if (MaxAllowedThreads>ASIZE(TaskQueue)) + MaxAllowedThreads=ASIZE(TaskQueue); + + Closing=false; + + bool Success = CriticalSectionCreate(&CritSection); +#ifdef _WIN_ALL + QueuedTasksCnt=CreateSemaphore(NULL,0,ASIZE(TaskQueue),NULL); + NoneActive=CreateEvent(NULL,TRUE,TRUE,NULL); + Success=Success && QueuedTasksCnt!=NULL && NoneActive!=NULL; +#elif defined(_UNIX) + AnyActive = false; + QueuedTasksCnt = 0; + Success=Success && pthread_cond_init(&AnyActiveCond,NULL)==0 && + pthread_mutex_init(&AnyActiveMutex,NULL)==0 && + pthread_cond_init(&QueuedTasksCntCond,NULL)==0 && + pthread_mutex_init(&QueuedTasksCntMutex,NULL)==0; +#endif + if (!Success) + { + ErrHandler.GeneralErrMsg(L"\nThread pool initialization failed."); + ErrHandler.Exit(RARX_FATAL); + } + + QueueTop = 0; + QueueBottom = 0; + ActiveThreads = 0; +} + + +ThreadPool::~ThreadPool() +{ + WaitDone(); + Closing=true; + +#ifdef _WIN_ALL + ReleaseSemaphore(QueuedTasksCnt,ASIZE(TaskQueue),NULL); +#elif defined(_UNIX) + // Threads still can access QueuedTasksCnt for a short time after WaitDone(), + // so lock is required. We would occassionally hang without it. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ASIZE(TaskQueue); + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); +#endif + + for(uint I=0;I<ThreadsCreatedCount;I++) + { +#ifdef _WIN_ALL + // Waiting until the thread terminates. + CWaitForSingleObject(ThreadHandles[I]); +#endif + // Close the thread handle. In Unix it results in pthread_join call, + // which also waits for thread termination. + ThreadClose(ThreadHandles[I]); + } + + CriticalSectionDelete(&CritSection); +#ifdef _WIN_ALL + CloseHandle(QueuedTasksCnt); + CloseHandle(NoneActive); +#elif defined(_UNIX) + pthread_cond_destroy(&AnyActiveCond); + pthread_mutex_destroy(&AnyActiveMutex); + pthread_cond_destroy(&QueuedTasksCntCond); + pthread_mutex_destroy(&QueuedTasksCntMutex); +#endif +} + + +void ThreadPool::CreateThreads() +{ + for(uint I=0;I<MaxAllowedThreads;I++) + { + ThreadHandles[I] = ThreadCreate(PoolThread, this); + ThreadsCreatedCount++; +#ifdef _WIN_ALL + if (ThreadPool::ThreadPriority!=THREAD_PRIORITY_NORMAL) + SetThreadPriority(ThreadHandles[I],ThreadPool::ThreadPriority); +#endif + } +} + + +NATIVE_THREAD_TYPE ThreadPool::PoolThread(void *Param) +{ + ((ThreadPool*)Param)->PoolThreadLoop(); + return 0; +} + + +void ThreadPool::PoolThreadLoop() +{ + QueueEntry Task; + while (GetQueuedTask(&Task)) + { + Task.Proc(Task.Param); + + CriticalSectionStart(&CritSection); + if (--ActiveThreads == 0) + { +#ifdef _WIN_ALL + SetEvent(NoneActive); +#elif defined(_UNIX) + pthread_mutex_lock(&AnyActiveMutex); + AnyActive=false; + pthread_cond_signal(&AnyActiveCond); + pthread_mutex_unlock(&AnyActiveMutex); +#endif + } + CriticalSectionEnd(&CritSection); + } +} + + +bool ThreadPool::GetQueuedTask(QueueEntry *Task) +{ +#ifdef _WIN_ALL + CWaitForSingleObject(QueuedTasksCnt); +#elif defined(_UNIX) + pthread_mutex_lock(&QueuedTasksCntMutex); + while (QueuedTasksCnt==0) + cpthread_cond_wait(&QueuedTasksCntCond,&QueuedTasksCntMutex); + QueuedTasksCnt--; + pthread_mutex_unlock(&QueuedTasksCntMutex); +#endif + + if (Closing) + return false; + + CriticalSectionStart(&CritSection); + + *Task = TaskQueue[QueueBottom]; + QueueBottom = (QueueBottom + 1) % ASIZE(TaskQueue); + + CriticalSectionEnd(&CritSection); + + return true; +} + + +// Add task to queue. We assume that it is always called from main thread, +// it allows to avoid any locks here. We process collected tasks only +// when WaitDone is called. +void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) +{ + if (ThreadsCreatedCount == 0) + CreateThreads(); + + // If queue is full, wait until it is empty. + if (ActiveThreads>=ASIZE(TaskQueue)) + WaitDone(); + + TaskQueue[QueueTop].Proc = Proc; + TaskQueue[QueueTop].Param = Data; + QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); + ActiveThreads++; +} + + +// Start queued tasks and wait until all threads are inactive. +// We assume that it is always called from main thread, when pool threads +// are sleeping yet. +void ThreadPool::WaitDone() +{ + if (ActiveThreads==0) + return; +#ifdef _WIN_ALL + ResetEvent(NoneActive); + ReleaseSemaphore(QueuedTasksCnt,ActiveThreads,NULL); + CWaitForSingleObject(NoneActive); +#elif defined(_UNIX) + AnyActive=true; + + // Threads reset AnyActive before accessing QueuedTasksCnt and even + // preceding WaitDone() call does not guarantee that some slow thread + // is not accessing QueuedTasksCnt now. So lock is necessary. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ActiveThreads; + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); + + pthread_mutex_lock(&AnyActiveMutex); + while (AnyActive) + cpthread_cond_wait(&AnyActiveCond,&AnyActiveMutex); + pthread_mutex_unlock(&AnyActiveMutex); +#endif +} +#endif // RAR_SMP diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.hpp new file mode 100644 index 00000000..85ed90dc --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/threadpool.hpp @@ -0,0 +1,107 @@ +#ifndef _RAR_THREADPOOL_ +#define _RAR_THREADPOOL_ + +#ifndef RAR_SMP +const uint MaxPoolThreads=1; // For single threaded version. +#else +// We need to use the processor groups API to increase it beyond 64. +// Also be sure to check and adjust if needed per thread and total block size +// when compressing if going above 64. +const uint MaxPoolThreads=64; + + +#ifdef _UNIX + #include <pthread.h> + #include <semaphore.h> +#endif + +// Undefine for debugging. +#define USE_THREADS + +#ifdef _UNIX + #define NATIVE_THREAD_TYPE void* + typedef void* (*NATIVE_THREAD_PTR)(void *Data); + typedef pthread_t THREAD_HANDLE; + typedef pthread_mutex_t CRITSECT_HANDLE; +#else + #define NATIVE_THREAD_TYPE DWORD WINAPI + typedef DWORD (WINAPI *NATIVE_THREAD_PTR)(void *Data); + typedef HANDLE THREAD_HANDLE; + typedef CRITICAL_SECTION CRITSECT_HANDLE; +#endif + +typedef void (*PTHREAD_PROC)(void *Data); +#define THREAD_PROC(fn) void fn(void *Data) + +uint GetNumberOfCPU(); +uint GetNumberOfThreads(); + + +class ThreadPool +{ + private: + struct QueueEntry + { + PTHREAD_PROC Proc; + void *Param; + }; + + void CreateThreads(); + static NATIVE_THREAD_TYPE PoolThread(void *Param); + void PoolThreadLoop(); + bool GetQueuedTask(QueueEntry *Task); + + // Number of threads in the pool. Must not exceed MaxPoolThreads. + uint MaxAllowedThreads; + THREAD_HANDLE ThreadHandles[MaxPoolThreads]; + + // Number of actually created threads. + uint ThreadsCreatedCount; + + uint ActiveThreads; + + QueueEntry TaskQueue[MaxPoolThreads]; + uint QueueTop; + uint QueueBottom; + + bool Closing; // Set true to quit all threads. + +#ifdef _WIN_ALL + // Semaphore counting number of tasks stored in queue. + HANDLE QueuedTasksCnt; + + // Event signalling if no active tasks are performing now. + HANDLE NoneActive; + +#elif defined(_UNIX) + // Semaphores seem to be slower than conditional variables in pthreads, + // so we use the conditional variable to count tasks stored in queue. + uint QueuedTasksCnt; + pthread_cond_t QueuedTasksCntCond; + pthread_mutex_t QueuedTasksCntMutex; + + bool AnyActive; // Active tasks present flag. + pthread_cond_t AnyActiveCond; + pthread_mutex_t AnyActiveMutex; +#endif + + // Pool critical section. We use the single section for all branches + // to avoid deadlocks, when thread1 has section1 and wants section2 + // and thread2 has section2 and wants section1. + CRITSECT_HANDLE CritSection; + public: + ThreadPool(uint MaxThreads); + ~ThreadPool(); + void AddTask(PTHREAD_PROC Proc,void *Data); + void WaitDone(); + +#ifdef _WIN_ALL + static int ThreadPriority; + static void SetPriority(int Priority) {ThreadPriority=Priority;} +#endif +}; + +#endif // RAR_SMP + +#endif // _RAR_THREADPOOL_ + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.cpp new file mode 100644 index 00000000..0abf49de --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.cpp @@ -0,0 +1,340 @@ +#include "rar.hpp" + +void RarTime::GetLocal(RarLocalTime *lt) +{ +#ifdef _WIN_ALL + FILETIME ft; + GetWinFT(&ft); + FILETIME lft; + + if (WinNT() < WNT_VISTA) + { + // SystemTimeToTzSpecificLocalTime based code produces 1 hour error on XP. + FileTimeToLocalFileTime(&ft,&lft); + } + else + { + // We use these functions instead of FileTimeToLocalFileTime according to + // MSDN recommendation: "To account for daylight saving time + // when converting a file time to a local time ..." + SYSTEMTIME st1,st2; + FileTimeToSystemTime(&ft,&st1); + SystemTimeToTzSpecificLocalTime(NULL,&st1,&st2); + SystemTimeToFileTime(&st2,&lft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st1,&rft); + uint64 Corrected=INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime); + lft.dwLowDateTime=(DWORD)Corrected; + lft.dwHighDateTime=(DWORD)(Corrected>>32); + } + + SYSTEMTIME st; + FileTimeToSystemTime(&lft,&st); + lt->Year=st.wYear; + lt->Month=st.wMonth; + lt->Day=st.wDay; + lt->Hour=st.wHour; + lt->Minute=st.wMinute; + lt->Second=st.wSecond; + lt->wDay=st.wDayOfWeek; + lt->yDay=lt->Day-1; + + static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; + for (uint I=1;I<lt->Month && I<=ASIZE(mdays);I++) + lt->yDay+=mdays[I-1]; + + if (lt->Month>2 && IsLeapYear(lt->Year)) + lt->yDay++; +#else + time_t ut=GetUnix(); + struct tm *t; + t=localtime(&ut); + + lt->Year=t->tm_year+1900; + lt->Month=t->tm_mon+1; + lt->Day=t->tm_mday; + lt->Hour=t->tm_hour; + lt->Minute=t->tm_min; + lt->Second=t->tm_sec; + lt->wDay=t->tm_wday; + lt->yDay=t->tm_yday; +#endif + lt->Reminder=(itime % TICKS_PER_SECOND); +} + + +void RarTime::SetLocal(RarLocalTime *lt) +{ +#ifdef _WIN_ALL + SYSTEMTIME st; + st.wYear=lt->Year; + st.wMonth=lt->Month; + st.wDay=lt->Day; + st.wHour=lt->Hour; + st.wMinute=lt->Minute; + st.wSecond=lt->Second; + st.wMilliseconds=0; + st.wDayOfWeek=0; + FILETIME lft; + if (SystemTimeToFileTime(&st,&lft)) + { + FILETIME ft; + + if (WinNT() < WNT_VISTA) + { + // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP. + LocalFileTimeToFileTime(&lft,&ft); + } + else + { + // Reverse procedure which we do in GetLocal. + SYSTEMTIME st1,st2; + FileTimeToSystemTime(&lft,&st2); // st2 might be unequal to st, because we added lt->Reminder to lft. + TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1); + SystemTimeToFileTime(&st1,&ft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st2,&rft); + uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime); + ft.dwLowDateTime=(DWORD)Corrected; + ft.dwHighDateTime=(DWORD)(Corrected>>32); + } + + SetWinFT(&ft); + } + else + Reset(); +#else + struct tm t; + + t.tm_sec=lt->Second; + t.tm_min=lt->Minute; + t.tm_hour=lt->Hour; + t.tm_mday=lt->Day; + t.tm_mon=lt->Month-1; + t.tm_year=lt->Year-1900; + t.tm_isdst=-1; + SetUnix(mktime(&t)); +#endif + itime+=lt->Reminder; +} + + + + +#ifdef _WIN_ALL +void RarTime::GetWinFT(FILETIME *ft) +{ + _ULARGE_INTEGER ul; + ul.QuadPart=GetWin(); + ft->dwLowDateTime=ul.LowPart; + ft->dwHighDateTime=ul.HighPart; +} + + +void RarTime::SetWinFT(FILETIME *ft) +{ + _ULARGE_INTEGER ul = {ft->dwLowDateTime, ft->dwHighDateTime}; + SetWin(ul.QuadPart); +} +#endif + + +// Get 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +uint64 RarTime::GetWin() +{ + return itime/(TICKS_PER_SECOND/10000000); +} + + +// Set 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +void RarTime::SetWin(uint64 WinTime) +{ + itime=WinTime*(TICKS_PER_SECOND/10000000); +} + + +time_t RarTime::GetUnix() +{ + return time_t(GetUnixNS()/1000000000); +} + + +void RarTime::SetUnix(time_t ut) +{ + if (sizeof(ut)>4) + SetUnixNS(uint64(ut)*1000000000); + else + { + // Convert 32-bit and possibly signed time_t to uint32 first, + // uint64 cast is not enough. Otherwise sign can expand to 64 bits. + SetUnixNS(uint64(uint32(ut))*1000000000); + } +} + + +// Get the high precision Unix time in nanoseconds since 01-01-1970. +uint64 RarTime::GetUnixNS() +{ + // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. + uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + return itime*(1000000000/TICKS_PER_SECOND)-ushift; +} + + +// Set the high precision Unix time in nanoseconds since 01-01-1970. +void RarTime::SetUnixNS(uint64 ns) +{ + // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. + uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND); +} + + +uint RarTime::GetDos() +{ + RarLocalTime lt; + GetLocal(<); + uint DosTime=(lt.Second/2)|(lt.Minute<<5)|(lt.Hour<<11)| + (lt.Day<<16)|(lt.Month<<21)|((lt.Year-1980)<<25); + return DosTime; +} + + +void RarTime::SetDos(uint DosTime) +{ + RarLocalTime lt; + lt.Second=(DosTime & 0x1f)*2; + lt.Minute=(DosTime>>5) & 0x3f; + lt.Hour=(DosTime>>11) & 0x1f; + lt.Day=(DosTime>>16) & 0x1f; + lt.Month=(DosTime>>21) & 0x0f; + lt.Year=(DosTime>>25)+1980; + lt.Reminder=0; + SetLocal(<); +} + + +void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS) +{ + if (IsSet()) + { + RarLocalTime lt; + GetLocal(<); + if (FullMS) + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u:%02u,%09u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute,lt.Second,lt.Reminder*(1000000000/TICKS_PER_SECOND)); + else + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute); + } + else + { + // We use escape before '?' to avoid weird C trigraph characters. + wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize); + } +} + + +#ifndef SFX_MODULE +void RarTime::SetIsoText(const wchar *TimeText) +{ + int Field[6]; + memset(Field,0,sizeof(Field)); + for (uint DigitCount=0;*TimeText!=0;TimeText++) + if (IsDigit(*TimeText)) + { + int FieldPos=DigitCount<4 ? 0:(DigitCount-4)/2+1; + if (FieldPos<ASIZE(Field)) + Field[FieldPos]=Field[FieldPos]*10+*TimeText-'0'; + DigitCount++; + } + RarLocalTime lt; + lt.Second=Field[5]; + lt.Minute=Field[4]; + lt.Hour=Field[3]; + lt.Day=Field[2]==0 ? 1:Field[2]; + lt.Month=Field[1]==0 ? 1:Field[1]; + lt.Year=Field[0]; + lt.Reminder=0; + SetLocal(<); +} +#endif + + +#ifndef SFX_MODULE +void RarTime::SetAgeText(const wchar *TimeText) +{ + uint Seconds=0,Value=0; + for (uint I=0;TimeText[I]!=0;I++) + { + wchar Ch=TimeText[I]; + if (IsDigit(Ch)) + Value=Value*10+Ch-'0'; + else + { + switch(etoupperw(Ch)) + { + case 'D': + Seconds+=Value*24*3600; + break; + case 'H': + Seconds+=Value*3600; + break; + case 'M': + Seconds+=Value*60; + break; + case 'S': + Seconds+=Value; + break; + } + Value=0; + } + } + SetCurrentTime(); + itime-=uint64(Seconds)*TICKS_PER_SECOND; +} +#endif + + +void RarTime::SetCurrentTime() +{ +#ifdef _WIN_ALL + FILETIME ft; + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st,&ft); + SetWinFT(&ft); +#else + time_t st; + time(&st); + SetUnix(st); +#endif +} + + +// Add the specified signed number of nanoseconds. +void RarTime::Adjust(int64 ns) +{ + ns/=1000000000/TICKS_PER_SECOND; // Convert ns to internal ticks. + itime+=(uint64)ns; +} + + +#ifndef SFX_MODULE +const wchar *GetMonthName(int Month) +{ + return uiGetMonthName(Month); +} +#endif + + +bool IsLeapYear(int Year) +{ + return (Year&3)==0 && (Year%100!=0 || Year%400==0); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.hpp new file mode 100644 index 00000000..52713616 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/timefn.hpp @@ -0,0 +1,65 @@ +#ifndef _RAR_TIMEFN_ +#define _RAR_TIMEFN_ + +struct RarLocalTime +{ + uint Year; + uint Month; + uint Day; + uint Hour; + uint Minute; + uint Second; + uint Reminder; // Part of time smaller than 1 second, represented in 1/REMINDER_PRECISION intervals. + uint wDay; + uint yDay; +}; + + +class RarTime +{ + private: + static const uint TICKS_PER_SECOND = 1000000000; // Internal precision. + + // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601. + // We use nanoseconds here to handle the high precision Unix time. + uint64 itime; + public: + // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND. + // Unlike TICKS_PER_SECOND, it is a public field. + static const uint REMINDER_PRECISION = TICKS_PER_SECOND; + public: + RarTime() {Reset();} + bool operator == (RarTime &rt) {return itime==rt.itime;} + bool operator != (RarTime &rt) {return itime!=rt.itime;} + bool operator < (RarTime &rt) {return itime<rt.itime;} + bool operator <= (RarTime &rt) {return itime<rt.itime || itime==rt.itime;} + bool operator > (RarTime &rt) {return itime>rt.itime;} + bool operator >= (RarTime &rt) {return itime>rt.itime || itime==rt.itime;} + + void GetLocal(RarLocalTime *lt); + void SetLocal(RarLocalTime *lt); +#ifdef _WIN_ALL + void GetWinFT(FILETIME *ft); + void SetWinFT(FILETIME *ft); +#endif + uint64 GetWin(); + void SetWin(uint64 WinTime); + time_t GetUnix(); + void SetUnix(time_t ut); + uint64 GetUnixNS(); + void SetUnixNS(uint64 ns); + uint GetDos(); + void SetDos(uint DosTime); + void GetText(wchar *DateStr,size_t MaxSize,bool FullMS); + void SetIsoText(const wchar *TimeText); + void SetAgeText(const wchar *TimeText); + void SetCurrentTime(); + void Reset() {itime=0;} + bool IsSet() {return itime!=0;} + void Adjust(int64 ns); +}; + +const wchar *GetMonthName(int Month); +bool IsLeapYear(int Year); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/ui.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/ui.cpp new file mode 100644 index 00000000..9713a887 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/ui.cpp @@ -0,0 +1,14 @@ +#include "rar.hpp" + +#include "uicommon.cpp" + +#ifdef SILENT +#include "uisilent.cpp" +#else + + + + +#include "uiconsole.cpp" + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/ui.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/ui.hpp new file mode 100644 index 00000000..8fc76aa8 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/ui.hpp @@ -0,0 +1,175 @@ +#ifndef _RAR_UI_ +#define _RAR_UI_ + +// UIERROR_ - error message; +// UIMSG_ - informational message; +// UIWAIT_ - message waiting for user confirmation; +// UIEVENT_ - if simple message is not enough; + +enum UIMESSAGE_CODE { + UIERROR_SYSERRMSG, UIERROR_GENERALERRMSG, UIERROR_INCERRCOUNT, + UIERROR_CHECKSUM, UIERROR_CHECKSUMENC, UIERROR_CHECKSUMPACKED, + UIERROR_BADPSW, UIERROR_MEMORY, UIERROR_FILEOPEN, UIERROR_FILECREATE, + UIERROR_FILECLOSE, UIERROR_FILESEEK, UIERROR_FILEREAD, UIERROR_FILEWRITE, + UIERROR_FILEDELETE, UIERROR_RECYCLEFAILED, UIERROR_FILERENAME, + UIERROR_FILEATTR, UIERROR_FILECOPY, UIERROR_FILECOPYHINT, + UIERROR_DIRCREATE, UIERROR_SLINKCREATE, UIERROR_HLINKCREATE, + UIERROR_NOLINKTARGET, UIERROR_NEEDADMIN, UIERROR_ARCBROKEN, + UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN, + UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN, + UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, + UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, + UIERROR_NOTSFX, UIERROR_OLDTOSFX, + UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM, + UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, + UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, + UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, + UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING, + UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF, + UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME, + UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED, + UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED, + UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT, + UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA, + UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX, + UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_EMAIL, + UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET, + UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, + UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET, + UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, + UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, + UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, + UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW,UIERROR_ADJUSTVALUE, + + UIMSG_FIRST, + UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, + UIMSG_RRFOUND, UIMSG_RRNOTFOUND, UIMSG_RRDAMAGED, UIMSG_BLOCKSRECOVERED, + UIMSG_COPYINGDATA, UIMSG_AREADAMAGED, UIMSG_SECTORDAMAGED, + UIMSG_SECTORRECOVERED, UIMSG_SECTORNOTRECOVERED, UIMSG_FOUND, + UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, + UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, + UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, + UIMSG_SKIPENCARC, + + UIWAIT_FIRST, + UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, + + UIEVENT_FIRST, + UIEVENT_SEARCHDUPFILESSTART, UIEVENT_SEARCHDUPFILESEND, + UIEVENT_CLEARATTRSTART, UIEVENT_CLEARATTRFILE, + UIEVENT_DELADDEDSTART, UIEVENT_DELADDEDFILE, UIEVENT_FILESFOUND, + UIEVENT_ERASEDISK, UIEVENT_FILESUMSTART, UIEVENT_FILESUMPROGRESS, + UIEVENT_FILESUMEND, UIEVENT_PROTECTSTART, UIEVENT_PROTECTEND, + UIEVENT_TESTADDEDSTART, UIEVENT_TESTADDEDEND, UIEVENT_RRTESTINGSTART, + UIEVENT_RRTESTINGEND, UIEVENT_NEWARCHIVE, UIEVENT_NEWREVFILE +}; + +// Flags for uiAskReplace function. +enum UIASKREP_FLAGS { + UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4 +}; + +// Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only +// UIASKREP_R_REPLACE, UIASKREP_R_SKIP and UIASKREP_R_CANCEL codes. +enum UIASKREP_RESULT { + UIASKREP_R_REPLACE,UIASKREP_R_SKIP,UIASKREP_R_REPLACEALL,UIASKREP_R_SKIPALL, + UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED +}; + +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); + +void uiInit(SOUND_NOTIFY_MODE Sound); + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName); +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip); +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize); +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); + +enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); +bool uiIsGlobalPasswordSet(); + +enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; +void uiAlarm(UIALARM_TYPE Type); + +void uiEolAfterMsg(); + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize); +#if !defined(SILENT) && !defined(SFX_MODULE) +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); +#endif +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull); + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month); +#endif + +class uiMsgStore +{ + private: + static const size_t MAX_MSG = 8; + const wchar *Str[MAX_MSG]; + uint Num[MAX_MSG]; + uint StrSize,NumSize; + UIMESSAGE_CODE Code; + public: + uiMsgStore(UIMESSAGE_CODE Code) + { + // Init arrays in case a caller passes fewer parameters than expected. + for (uint I=0;I<ASIZE(Str);I++) + Str[I]=L""; + memset(Num,0,sizeof(Num)); + + NumSize=StrSize=0; + this->Code=Code; + } + uiMsgStore& operator << (const wchar *s) + { + if (StrSize<MAX_MSG) + Str[StrSize++]=s; + return *this; + } + uiMsgStore& operator << (uint n) + { + if (NumSize<MAX_MSG) + Num[NumSize++]=n; + return *this; + } + + void Msg(); +}; + + +// Templates recognize usual NULL as integer, not wchar*. +#define UINULL ((wchar *)NULL) + +inline void uiMsg(UIMESSAGE_CODE Code) +{ + uiMsgStore Store(Code); + Store.Msg(); +} + +template<class T1> void uiMsg(UIMESSAGE_CODE Code,T1 a1) +{ + uiMsgStore Store(Code); + Store<<a1; + Store.Msg(); +} + +template<class T1,class T2> void uiMsg(UIMESSAGE_CODE Code,T1 a1,T2 a2) +{ + uiMsgStore Store(Code); + Store<<a1<<a2; + Store.Msg(); +} + +template<class T1,class T2,class T3> void uiMsg(UIMESSAGE_CODE code,T1 a1,T2 a2,T3 a3) +{ + uiMsgStore Store(code); + Store<<a1<<a2<<a3; + Store.Msg(); +} + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/uicommon.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/uicommon.cpp new file mode 100644 index 00000000..2c4fcda4 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/uicommon.cpp @@ -0,0 +1,65 @@ +static SOUND_NOTIFY_MODE uiSoundNotify; + +void uiInit(SOUND_NOTIFY_MODE Sound) +{ + uiSoundNotify = Sound; +} + + +// Additionally to handling user input, it analyzes and sets command options. +// Returns only 'replace', 'skip' and 'cancel' codes. +UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + if (Cmd->Overwrite==OVERWRITE_NONE) + return UIASKREP_R_SKIP; + +#if !defined(SFX_MODULE) && !defined(SILENT) + // Must be before Cmd->AllYes check or -y switch would override -or. + if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize)) + return UIASKREP_R_REPLACE; +#endif + + // This check must be after OVERWRITE_AUTORENAME processing or -y switch + // would override -or. + if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL) + { + PrepareToDelete(Name); + return UIASKREP_R_REPLACE; + } + + wchar NewName[NM]; + wcsncpyz(NewName,Name,ASIZE(NewName)); + UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags); + + if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) + PrepareToDelete(Name); + + if (Choice==UIASKREP_R_REPLACEALL) + { + Cmd->Overwrite=OVERWRITE_ALL; + return UIASKREP_R_REPLACE; + } + if (Choice==UIASKREP_R_SKIPALL) + { + Cmd->Overwrite=OVERWRITE_NONE; + return UIASKREP_R_SKIP; + } + if (Choice==UIASKREP_R_RENAME) + { + if (PointToName(NewName)==NewName) + SetName(Name,NewName,MaxNameSize); + else + wcsncpyz(Name,NewName,MaxNameSize); + if (FileExist(Name)) + return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags); + return UIASKREP_R_REPLACE; + } +#if !defined(SFX_MODULE) && !defined(SILENT) + if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize)) + { + Cmd->Overwrite=OVERWRITE_AUTORENAME; + return UIASKREP_R_REPLACE; + } +#endif + return Choice; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/uiconsole.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/uiconsole.cpp new file mode 100644 index 00000000..d713fac0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/uiconsole.cpp @@ -0,0 +1,481 @@ +static bool AnyMessageDisplayed=false; // For console -idn switch. + +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; + + FindData ExistingFD; + memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails. + FindFile::FastFind(Name,&ExistingFD); + itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1)); + ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false); + + if (FileSize==INT64NDF || FileTime==NULL) + { + eprintf(L"\n"); + eprintf(St(MAskOverwrite),Name); + } + else + { + itoa(FileSize,SizeText2,ASIZE(SizeText2)); + FileTime->GetText(DateStr2,ASIZE(DateStr2),false); + if ((Flags & UIASKREP_F_EXCHSRCDEST)==0) + eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2); + else + eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1); + } + + bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0; + int Choice=0; + do + { + Choice=Ask(St(AllowRename ? MYesNoAllRenQ : MYesNoAllQ)); + } while (Choice==0); // 0 means invalid input. + switch(Choice) + { + case 1: + return UIASKREP_R_REPLACE; + case 2: + return UIASKREP_R_SKIP; + case 3: + return UIASKREP_R_REPLACEALL; + case 4: + return UIASKREP_R_SKIPALL; + } + if (AllowRename && Choice==5) + { + mprintf(St(MAskNewName)); + if (getwstr(Name,MaxNameSize)) + return UIASKREP_R_RENAME; + else + return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'. + } + return UIASKREP_R_CANCEL; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ + mprintf(St(Extract ? MExtracting : MExtrTest), ArcName); +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ + // We set the total size to 0 to update only the current progress and keep + // the total progress intact in WinRAR. Unlike WinRAR, console RAR has only + // the total progress and updates it with current values in such case. + int CurPercent=TotalSize!=0 ? ToPercent(CurSize,TotalSize) : ToPercent(CurFileSize,TotalFileSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ + int CurPercent=ToPercent(CurSize,TotalSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiMsgStore::Msg() +{ + // When creating volumes, AnyMessageDisplayed must be reset for UIEVENT_NEWARCHIVE, + // so it ignores this and all earlier messages like UIEVENT_PROTECTEND + // and UIEVENT_PROTECTEND, because they precede "Creating archive" message + // and do not interfere with -idn and file names. If we do not ignore them, + // uiEolAfterMsg() in uiStartFileAddit() can cause unneeded carriage return + // in archiving percent after creating a new volume with -v -idn (and -rr + // for UIEVENT_PROTECT*) switches. AnyMessageDisplayed is set for messages + // after UIEVENT_NEWARCHIVE, so archiving percent with -idn is moved to + // next line and does not delete their last characters. + // Similarly we ignore UIEVENT_RRTESTINGEND for volumes, because it is issued + // before "Testing archive" and would add an excessive \n otherwise. + AnyMessageDisplayed=(Code!=UIEVENT_NEWARCHIVE && Code!=UIEVENT_RRTESTINGEND); + + switch(Code) + { + case UIERROR_SYSERRMSG: + case UIERROR_GENERALERRMSG: + Log(NULL,L"\n%ls",Str[0]); + break; + case UIERROR_CHECKSUM: + Log(Str[0],St(MCRCFailed),Str[1]); + break; + case UIERROR_CHECKSUMENC: + Log(Str[0],St(MEncrBadCRC),Str[1]); + break; + case UIERROR_CHECKSUMPACKED: + Log(Str[0],St(MDataBadCRC),Str[1],Str[0]); + break; + case UIERROR_BADPSW: + Log(Str[0],St(MWrongFilePassword),Str[1]); + break; + case UIWAIT_BADPSW: + Log(Str[0],St(MWrongPassword)); + break; + case UIERROR_MEMORY: + mprintf(L"\n"); + Log(NULL,St(MErrOutMem)); + break; + case UIERROR_FILEOPEN: + Log(Str[0],St(MCannotOpen),Str[1]); + break; + case UIERROR_FILECREATE: + Log(Str[0],St(MCannotCreate),Str[1]); + break; + case UIERROR_FILECLOSE: + Log(NULL,St(MErrFClose),Str[0]); + break; + case UIERROR_FILESEEK: + Log(NULL,St(MErrSeek),Str[0]); + break; + case UIERROR_FILEREAD: + mprintf(L"\n"); + Log(Str[0],St(MErrRead),Str[1]); + break; + case UIERROR_FILEWRITE: + Log(Str[0],St(MErrWrite),Str[1]); + break; +#ifndef SFX_MODULE + case UIERROR_FILEDELETE: + Log(Str[0],St(MCannotDelete),Str[1]); + break; + case UIERROR_RECYCLEFAILED: + Log(Str[0],St(MRecycleFailed)); + break; + case UIERROR_FILERENAME: + Log(Str[0],St(MErrRename),Str[1],Str[2]); + break; +#endif + case UIERROR_FILEATTR: + Log(Str[0],St(MErrChangeAttr),Str[1]); + break; + case UIERROR_FILECOPY: + Log(Str[0],St(MCopyError),Str[1],Str[2]); + break; + case UIERROR_FILECOPYHINT: + Log(Str[0],St(MCopyErrorHint)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_DIRCREATE: + Log(Str[0],St(MExtrErrMkDir),Str[1]); + break; + case UIERROR_SLINKCREATE: + Log(Str[0],St(MErrCreateLnkS),Str[1]); + break; + case UIERROR_HLINKCREATE: + Log(NULL,St(MErrCreateLnkH),Str[0]); + break; + case UIERROR_NOLINKTARGET: + Log(NULL,St(MErrLnkTarget)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_NEEDADMIN: + Log(NULL,St(MNeedAdmin)); + break; + case UIERROR_ARCBROKEN: + Log(Str[0],St(MErrBrokenArc)); + break; + case UIERROR_HEADERBROKEN: + Log(Str[0],St(MHeaderBroken)); + break; + case UIERROR_MHEADERBROKEN: + Log(Str[0],St(MMainHeaderBroken)); + break; + case UIERROR_FHEADERBROKEN: + Log(Str[0],St(MLogFileHead),Str[1]); + break; + case UIERROR_SUBHEADERBROKEN: + Log(Str[0],St(MSubHeadCorrupt)); + break; + case UIERROR_SUBHEADERUNKNOWN: + Log(Str[0],St(MSubHeadUnknown)); + break; + case UIERROR_SUBHEADERDATABROKEN: + Log(Str[0],St(MSubHeadDataCRC),Str[1]); + break; + case UIERROR_RRDAMAGED: + Log(Str[0],St(MRRDamaged)); + break; + case UIERROR_UNKNOWNMETHOD: + Log(Str[0],St(MUnknownMeth),Str[1]); + break; + case UIERROR_UNKNOWNENCMETHOD: + { + wchar Msg[256]; + swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]); + Log(Str[0],L"%s: %s",Msg,Str[2]); + } + break; +#ifndef SFX_MODULE + case UIERROR_RENAMING: + Log(Str[0],St(MRenaming),Str[1],Str[2]); + break; + case UIERROR_NEWERRAR: + Log(Str[0],St(MNewerRAR)); + break; +#endif + case UIERROR_RECVOLDIFFSETS: + Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); + break; + case UIERROR_RECVOLALLEXIST: + mprintf(St(MRecVolAllExist)); + break; + case UIERROR_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIERROR_RECVOLCANNOTFIX: + mprintf(St(MRecVolCannotFix)); + break; + case UIERROR_UNEXPEOF: + Log(Str[0],St(MLogUnexpEOF)); + break; + case UIERROR_BADARCHIVE: + Log(Str[0],St(MBadArc),Str[0]); + break; + case UIERROR_CMTBROKEN: + Log(Str[0],St(MLogCommBrk)); + break; + case UIERROR_INVALIDNAME: + Log(Str[0],St(MInvalidName),Str[1]); + mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile. + break; +#ifndef SFX_MODULE + case UIERROR_OPFAILED: + Log(NULL,St(MOpFailed)); + break; + case UIERROR_NEWRARFORMAT: + Log(Str[0],St(MNewRarFormat)); + break; +#endif + case UIERROR_NOFILESTOEXTRACT: + mprintf(St(MExtrNoFiles)); + break; + case UIERROR_MISSINGVOL: + Log(Str[0],St(MAbsNextVol),Str[0]); + break; +#ifndef SFX_MODULE + case UIERROR_NEEDPREVVOL: + Log(Str[0],St(MUnpCannotMerge),Str[1]); + break; + case UIERROR_UNKNOWNEXTRA: + Log(Str[0],St(MUnknownExtra),Str[1]); + break; + case UIERROR_CORRUPTEXTRA: + Log(Str[0],St(MCorruptExtra),Str[1],Str[2]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_NTFSREQUIRED: + Log(NULL,St(MNTFSRequired),Str[0]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_ACLBROKEN: + Log(Str[0],St(MACLBroken),Str[1]); + break; + case UIERROR_ACLUNKNOWN: + Log(Str[0],St(MACLUnknown),Str[1]); + break; + case UIERROR_ACLSET: + Log(Str[0],St(MACLSetError),Str[1]); + break; + case UIERROR_STREAMBROKEN: + Log(Str[0],St(MStreamBroken),Str[1]); + break; + case UIERROR_STREAMUNKNOWN: + Log(Str[0],St(MStreamUnknown),Str[1]); + break; +#endif + case UIERROR_INCOMPATSWITCH: + mprintf(St(MIncompatSwitch),Str[0],Num[0]); + break; + case UIERROR_PATHTOOLONG: + Log(NULL,L"\n%ls%ls%ls",Str[0],Str[1],Str[2]); + Log(NULL,St(MPathTooLong)); + break; +#ifndef SFX_MODULE + case UIERROR_DIRSCAN: + Log(NULL,St(MScanError),Str[0]); + break; +#endif + case UIERROR_UOWNERBROKEN: + Log(Str[0],St(MOwnersBroken),Str[1]); + break; + case UIERROR_UOWNERGETOWNERID: + Log(Str[0],St(MErrGetOwnerID),Str[1]); + break; + case UIERROR_UOWNERGETGROUPID: + Log(Str[0],St(MErrGetGroupID),Str[1]); + break; + case UIERROR_UOWNERSET: + Log(Str[0],St(MSetOwnersError),Str[1]); + break; + case UIERROR_ULINKREAD: + Log(NULL,St(MErrLnkRead),Str[0]); + break; + case UIERROR_ULINKEXIST: + Log(NULL,St(MSymLinkExists),Str[0]); + break; + case UIERROR_READERRTRUNCATED: + Log(NULL,St(MErrReadTrunc),Str[0]); + break; + case UIERROR_READERRCOUNT: + Log(NULL,St(MErrReadCount),Num[0]); + break; + case UIERROR_DIRNAMEEXISTS: + Log(NULL,St(MDirNameExists)); + break; + case UIERROR_TRUNCPSW: + eprintf(St(MTruncPsw),Num[0]); + eprintf(L"\n"); + break; + case UIERROR_ADJUSTVALUE: + Log(NULL,St(MAdjustValue),Str[0],Str[1]); + break; + +#ifndef SFX_MODULE + case UIMSG_STRING: + mprintf(L"\n%s",Str[0]); + break; +#endif + case UIMSG_CORRECTINGNAME: + Log(Str[0],St(MCorrectingName)); + break; + case UIMSG_BADARCHIVE: + mprintf(St(MBadArc),Str[0]); + break; + case UIMSG_CREATING: + mprintf(St(MCreating),Str[0]); + break; + case UIMSG_RENAMING: + mprintf(St(MRenaming),Str[0],Str[1]); + break; + case UIMSG_RECVOLCALCCHECKSUM: + mprintf(St(MCalcCRCAllVol)); + break; + case UIMSG_RECVOLFOUND: + mprintf(St(MRecVolFound),Num[0]); + break; + case UIMSG_RECVOLMISSING: + mprintf(St(MRecVolMissing),Num[0]); + break; + case UIMSG_MISSINGVOL: + mprintf(St(MAbsNextVol),Str[0]); + break; + case UIMSG_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIMSG_CHECKSUM: + mprintf(St(MCRCFailed),Str[0]); + break; + case UIMSG_FAT32SIZE: + mprintf(St(MFAT32Size)); + mprintf(L" "); // For progress percent. + break; + case UIMSG_SKIPENCARC: + Log(NULL,St(MSkipEncArc),Str[0]); + break; + + + + case UIEVENT_RRTESTINGSTART: + mprintf(L"%s ",St(MTestingRR)); + break; + } +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + // Unlike GUI we cannot provide Cancel button here, so we use the empty + // password to abort. Otherwise user not knowing a password would need to + // press Ctrl+C multiple times to quit from infinite password request loop. + return GetConsolePassword(Type,FileName,Password) && Password->IsSet(); +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ + if (uiSoundNotify==SOUND_NOTIFY_ON) + { + static clock_t LastTime=-10; // Negative to always beep first time. + if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5) + { +#ifdef _WIN_ALL + MessageBeep(-1); +#else + putwchar('\007'); +#endif + LastTime=MonoClock(); + } + } +} + + + + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize) +{ + eprintf(St(MAskNextVol),VolName); + return Ask(St(MContinueQuit))!=2; +} + + +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) +{ + eprintf(St(MErrReadInfo)); + int Code=Ask(St(MIgnoreAllRetryQuit)); + + Ignore=(Code==1); + All=(Code==2); + Quit=(Code==4); + Retry=!Ignore && !All && !Quit; // Default also for invalid input, not just for 'Retry'. +} + + +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull) +{ + mprintf(L"\n"); + Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName); + return Ask(St(MRetryAbort))==1; +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + static MSGID MonthID[12]={ + MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, + MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec + }; + return St(MonthID[Month]); +} +#endif + + +void uiEolAfterMsg() +{ + if (AnyMessageDisplayed) + { + // Avoid deleting several last characters of any previous error message + // with percentage indicator in -idn mode. + AnyMessageDisplayed=false; + mprintf(L"\n"); + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/uisilent.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/uisilent.cpp new file mode 100644 index 00000000..1df09756 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/uisilent.cpp @@ -0,0 +1,74 @@ +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + return UIASKREP_R_REPLACE; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ +} + + +void uiMsgStore::Msg() +{ +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + return false; +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ +} + + +bool uiIsAborted() +{ + return false; +} + + +void uiGiveTick() +{ +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + return L""; +} +#endif + + +void uiEolAfterMsg() +{ +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/ulinks.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/ulinks.cpp new file mode 100644 index 00000000..d198f2e0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/ulinks.cpp @@ -0,0 +1,110 @@ + + +static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) +{ + CreatePath(LinkName,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. + DelFile(LinkName); + + char LinkNameA[NM]; + WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); + if (symlink(Target,LinkNameA)==-1) // Error. + { + if (errno==EEXIST) + uiMsg(UIERROR_ULINKEXIST,LinkName); + else + { + uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName); + ErrHandler.SetErrorCode(RARX_WARNING); + } + return false; + } +#ifdef USE_LUTIMES +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=fta->GetUnix(); + times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=ftm->GetUnix(); + times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW); +#else + struct timeval tv[2]; + tv[0].tv_sec=fta->GetUnix(); + tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000); + tv[1].tv_sec=ftm->GetUnix(); + tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000); + lutimes(LinkNameA,tv); +#endif +#endif + + return true; +} + + +static bool IsFullPath(const char *PathA) // Unix ASCII version. +{ + return *PathA==CPATHDIVIDER; +} + + +bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +{ + char Target[NM]; + if (IsLink(Arc.FileHead.FileAttr)) + { + size_t DataSize=(size_t)Arc.FileHead.PackSize; + if (DataSize>ASIZE(Target)-1) + return false; + if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize) + return false; + Target[DataSize]=0; + + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); + DataIO.UnpHash.Update(Target,strlen(Target)); + DataIO.UnpHash.Result(&Arc.FileHead.FileHash); + + // Return true in case of bad checksum, so link will be processed further + // and extraction routine will report the checksum error. + if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL)) + return true; + + wchar TargetW[NM]; + CharToWide(Target,TargetW,ASIZE(TargetW)); + // Check for *TargetW==0 to catch CharToWide failure. + // Use Arc.FileHead.FileName instead of LinkName, since LinkName + // can include the destination path as a prefix, which can + // confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (*TargetW==0 || IsFullPath(TargetW) || + !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) + return false; + return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); + } + return false; +} + + +bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + char Target[NM]; + WideToChar(hd->RedirName,Target,ASIZE(Target)); + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) + { + // Cannot create Windows absolute path symlinks in Unix. Only relative path + // Windows symlinks can be created here. RAR 5.0 used \??\ prefix + // for Windows absolute symlinks, since RAR 5.1 /??/ is used. + // We escape ? as \? to avoid "trigraph" warning + if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0) + return false; + DosSlashToUnix(Target,Target,ASIZE(Target)); + } + // Use hd->FileName instead of LinkName, since LinkName can include + // the destination path as a prefix, which can confuse + // IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (IsFullPath(Target) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + return false; + return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.cpp new file mode 100644 index 00000000..5421923f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.cpp @@ -0,0 +1,606 @@ +#include "rar.hpp" +#define MBFUNCTIONS + +#if defined(_UNIX) && defined(MBFUNCTIONS) + +static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success); +static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success); + +// In Unix we map high ASCII characters which cannot be converted to Unicode +// to 0xE000 - 0xE0FF private use Unicode area. +static const uint MapAreaStart=0xE000; + +// Mapped string marker. Initially we used 0xFFFF for this purpose, +// but it causes MSVC2008 swprintf to fail (it treats 0xFFFF as error marker). +// While we could workaround it, it is safer to use another character. +static const uint MappedStringMark=0xFFFE; + +#endif + +bool WideToChar(const wchar *Src,char *Dest,size_t DestSize) +{ + bool RetCode=true; + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. + +#ifdef _WIN_ALL + if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,(int)DestSize,NULL,NULL)==0) + RetCode=false; + +// wcstombs is broken in Android NDK r9. +#elif defined(_APPLE) + WideToUtf(Src,Dest,DestSize); + +#elif defined(MBFUNCTIONS) + if (!WideToCharMap(Src,Dest,DestSize,RetCode)) + { + mbstate_t ps; // Use thread safe external state based functions. + memset (&ps, 0, sizeof(ps)); + const wchar *SrcParam=Src; // wcsrtombs can change the pointer. + + // Some implementations of wcsrtombs can cause memory analyzing tools + // like valgrind to report uninitialized data access. It happens because + // internally these implementations call SSE4 based wcslen function, + // which reads 16 bytes at once including those beyond of trailing 0. + size_t ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); + + if (ResultingSize==(size_t)-1 && errno==EILSEQ) + { + // Aborted on inconvertible character not zero terminating the result. + // EILSEQ helps to distinguish it from small output buffer abort. + // We want to convert as much as we can, so we clean the output buffer + // and repeat conversion. + memset (&ps, 0, sizeof(ps)); + SrcParam=Src; // wcsrtombs can change the pointer. + memset(Dest,0,DestSize); + ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); + } + + if (ResultingSize==(size_t)-1) + RetCode=false; + if (ResultingSize==0 && *Src!=0) + RetCode=false; + } +#else + for (int I=0;I<DestSize;I++) + { + Dest[I]=(char)Src[I]; + if (Src[I]==0) + break; + } +#endif + if (DestSize>0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. WideCharToMultiByte returns 'failed' code + // and partially converted string even if we wanted to convert only a part + // of string and passed DestSize smaller than required for fully converted + // string. Such call is the valid behavior in RAR code and we do not expect + // the empty string in this case. + + return RetCode; +} + + +bool CharToWide(const char *Src,wchar *Dest,size_t DestSize) +{ + bool RetCode=true; + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. + +#ifdef _WIN_ALL + if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,(int)DestSize)==0) + RetCode=false; + +// mbstowcs is broken in Android NDK r9. +#elif defined(_APPLE) + UtfToWide(Src,Dest,DestSize); + +#elif defined(MBFUNCTIONS) + mbstate_t ps; + memset (&ps, 0, sizeof(ps)); + const char *SrcParam=Src; // mbsrtowcs can change the pointer. + size_t ResultingSize=mbsrtowcs(Dest,&SrcParam,DestSize,&ps); + if (ResultingSize==(size_t)-1) + RetCode=false; + if (ResultingSize==0 && *Src!=0) + RetCode=false; + + if (RetCode==false && DestSize>1) + CharToWideMap(Src,Dest,DestSize,RetCode); +#else + for (int I=0;I<DestSize;I++) + { + Dest[I]=(wchar_t)Src[I]; + if (Src[I]==0) + break; + } +#endif + if (DestSize>0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. MultiByteToWideChar returns 'failed' code + // even if we wanted to convert only a part of string and passed DestSize + // smaller than required for fully converted string. Such call is the valid + // behavior in RAR code and we do not expect the empty string in this case. + + return RetCode; +} + + +#if defined(_UNIX) && defined(MBFUNCTIONS) +// Convert and restore mapped inconvertible Unicode characters. +// We use it for extended ASCII names in Unix. +bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success) +{ + // String with inconvertible characters mapped to private use Unicode area + // must have the mark code somewhere. + if (wcschr(Src,(wchar)MappedStringMark)==NULL) + return false; + + // Seems to be that wcrtomb in some memory analyzing libraries + // can produce uninitilized output while reporting success on garbage input. + // So we clean the destination to calm analyzers. + memset(Dest,0,DestSize); + + Success=true; + uint SrcPos=0,DestPos=0; + while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX) + { + if (uint(Src[SrcPos])==MappedStringMark) + { + SrcPos++; + continue; + } + // For security reasons do not restore low ASCII codes, so mapping cannot + // be used to hide control codes like path separators. + if (uint(Src[SrcPos])>=MapAreaStart+0x80 && uint(Src[SrcPos])<MapAreaStart+0x100) + Dest[DestPos++]=char(uint(Src[SrcPos++])-MapAreaStart); + else + { + mbstate_t ps; + memset(&ps,0,sizeof(ps)); + if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==(size_t)-1) + { + Dest[DestPos]='_'; + Success=false; + } + SrcPos++; + memset(&ps,0,sizeof(ps)); + int Length=mbrlen(Dest+DestPos,MB_CUR_MAX,&ps); + DestPos+=Max(Length,1); + } + } + Dest[Min(DestPos,DestSize-1)]=0; + return true; +} +#endif + + +#if defined(_UNIX) && defined(MBFUNCTIONS) +// Convert and map inconvertible Unicode characters. +// We use it for extended ASCII names in Unix. +void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success) +{ + // Map inconvertible characters to private use Unicode area 0xE000. + // Mark such string by placing special non-character code before + // first inconvertible character. + Success=false; + bool MarkAdded=false; + uint SrcPos=0,DestPos=0; + while (DestPos<DestSize) + { + if (Src[SrcPos]==0) + { + Success=true; + break; + } + mbstate_t ps; + memset(&ps,0,sizeof(ps)); + size_t res=mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps); + if (res==(size_t)-1 || res==(size_t)-2) + { + // For security reasons we do not want to map low ASCII characters, + // so we do not have additional .. and path separator codes. + if (byte(Src[SrcPos])>=0x80) + { + if (!MarkAdded) + { + Dest[DestPos++]=MappedStringMark; + MarkAdded=true; + if (DestPos>=DestSize) + break; + } + Dest[DestPos++]=byte(Src[SrcPos++])+MapAreaStart; + } + else + break; + } + else + { + memset(&ps,0,sizeof(ps)); + int Length=mbrlen(Src+SrcPos,MB_CUR_MAX,&ps); + SrcPos+=Max(Length,1); + DestPos++; + } + } + Dest[Min(DestPos,DestSize-1)]=0; +} +#endif + + +// SrcSize is in wide characters, not in bytes. +byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize) +{ + for (size_t I=0;I<SrcSize;I++,Src++) + { + Dest[I*2]=(byte)*Src; + Dest[I*2+1]=(byte)(*Src>>8); + if (*Src==0) + break; + } + return Dest; +} + + +wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) +{ + for (size_t I=0;I<DestSize;I++) + if ((Dest[I]=Src[I*2]+(Src[I*2+1]<<8))==0) + break; + return Dest; +} + + +void WideToUtf(const wchar *Src,char *Dest,size_t DestSize) +{ + long dsize=(long)DestSize; + dsize--; + while (*Src!=0 && --dsize>=0) + { + uint c=*(Src++); + if (c<0x80) + *(Dest++)=c; + else + if (c<0x800 && --dsize>=0) + { + *(Dest++)=(0xc0|(c>>6)); + *(Dest++)=(0x80|(c&0x3f)); + } + else + { + if (c>=0xd800 && c<=0xdbff && *Src>=0xdc00 && *Src<=0xdfff) // Surrogate pair. + { + c=((c-0xd800)<<10)+(*Src-0xdc00)+0x10000; + Src++; + } + if (c<0x10000 && (dsize-=2)>=0) + { + *(Dest++)=(0xe0|(c>>12)); + *(Dest++)=(0x80|((c>>6)&0x3f)); + *(Dest++)=(0x80|(c&0x3f)); + } + else + if (c < 0x200000 && (dsize-=3)>=0) + { + *(Dest++)=(0xf0|(c>>18)); + *(Dest++)=(0x80|((c>>12)&0x3f)); + *(Dest++)=(0x80|((c>>6)&0x3f)); + *(Dest++)=(0x80|(c&0x3f)); + } + } + } + *Dest=0; +} + + +size_t WideToUtfSize(const wchar *Src) +{ + size_t Size=0; + for (;*Src!=0;Src++) + if (*Src<0x80) + Size++; + else + if (*Src<0x800) + Size+=2; + else + if ((uint)*Src<0x10000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + { + if (Src[0]>=0xd800 && Src[0]<=0xdbff && Src[1]>=0xdc00 && Src[1]<=0xdfff) + { + Size+=4; // 4 output bytes for Unicode surrogate pair. + Src++; + } + else + Size+=3; + } + else + if ((uint)*Src<0x200000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + Size+=4; + return Size+1; // Include terminating zero. +} + + +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize) +{ + bool Success=true; + long dsize=(long)DestSize; + dsize--; + while (*Src!=0) + { + uint c=byte(*(Src++)),d; + if (c<0x80) + d=c; + else + if ((c>>5)==6) + { + if ((*Src&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0x1f)<<6)|(*Src&0x3f); + Src++; + } + else + if ((c>>4)==14) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); + Src+=2; + } + else + if ((c>>3)==30) + { + if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) + { + Success=false; + break; + } + d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); + Src+=3; + } + else + { + Success=false; + break; + } + if (--dsize<0) + break; + if (d>0xffff) + { + if (--dsize<0) + break; + if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. + { + Success=false; + continue; + } + if (sizeof(*Dest)==2) // Use the surrogate pair. + { + *(Dest++)=((d-0x10000)>>10)+0xd800; + *(Dest++)=(d&0x3ff)+0xdc00; + } + else + *(Dest++)=d; + } + else + *(Dest++)=d; + } + *Dest=0; + return Success; +} + + +// For zero terminated strings. +bool IsTextUtf8(const byte *Src) +{ + return IsTextUtf8(Src,strlen((const char *)Src)); +} + + +// Source data can be both with and without UTF-8 BOM. +bool IsTextUtf8(const byte *Src,size_t SrcSize) +{ + while (SrcSize-- > 0) + { + byte C=*(Src++); + int HighOne=0; // Number of leftmost '1' bits. + for (byte Mask=0x80;Mask!=0 && (C & Mask)!=0;Mask>>=1) + HighOne++; + if (HighOne==1 || HighOne>6) + return false; + while (--HighOne > 0) + if (SrcSize-- <= 0 || (*(Src++) & 0xc0)!=0x80) + return false; + } + return true; +} + + +int wcsicomp(const wchar *s1,const wchar *s2) +{ +#ifdef _WIN_ALL + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; +#else + while (true) + { + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + if (*s1==0) + break; + s1++; + s2++; + } + return 0; +#endif +} + + +int wcsnicomp(const wchar *s1,const wchar *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. + size_t l1=Min(wcslen(s1)+1,n); + size_t l2=Min(wcslen(s2)+1,n); + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; +#else + if (n==0) + return 0; + while (true) + { + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + if (*s1==0 || --n==0) + break; + s1++; + s2++; + } + return 0; +#endif +} + + +// Case insensitive wcsstr(). +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) +{ + for (size_t i=0;str[i]!=0;i++) + for (size_t j=0;;j++) + { + if (search[j]==0) + return str+i; + if (tolowerw(str[i+j])!=tolowerw(search[j])) + break; + } + return NULL; +} + + +#ifndef SFX_MODULE +wchar* wcslower(wchar *s) +{ +#ifdef _WIN_ALL + // _wcslwr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharLower involves less overhead. + CharLower(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towlower(*c); +#endif + return s; +} +#endif + + +#ifndef SFX_MODULE +wchar* wcsupper(wchar *s) +{ +#ifdef _WIN_ALL + // _wcsupr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharUpper involves less overhead. + CharUpper(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towupper(*c); +#endif + return s; +} +#endif + + + + +int toupperw(int ch) +{ +#if defined(_WIN_ALL) + // CharUpper is more reliable than towupper in Windows, which seems to be + // C locale dependent even in Unicode version. For example, towupper failed + // to convert lowercase Russian characters. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towupper(ch); +#endif +} + + +int tolowerw(int ch) +{ +#if defined(_WIN_ALL) + // CharLower is more reliable than towlower in Windows. + // See comment for towupper above. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towlower(ch); +#endif +} + + +int atoiw(const wchar *s) +{ + return (int)atoilw(s); +} + + +int64 atoilw(const wchar *s) +{ + bool sign=false; + if (*s=='-') // We do use signed integers here, for example, in GUI SFX. + { + s++; + sign=true; + } + // Use unsigned type here, since long string can overflow the variable + // and signed integer overflow is undefined behavior in C++. + uint64 n=0; + while (*s>='0' && *s<='9') + { + n=n*10+(*s-'0'); + s++; + } + // Check int64(n)>=0 to avoid the signed overflow with undefined behavior + // when negating 0x8000000000000000. + return sign && int64(n)>=0 ? -int64(n) : int64(n); +} + + +#ifdef DBCS_SUPPORTED +SupportDBCS gdbcs; + +SupportDBCS::SupportDBCS() +{ + Init(); +} + + +void SupportDBCS::Init() +{ + CPINFO CPInfo; + GetCPInfo(CP_ACP,&CPInfo); + DBCSMode=CPInfo.MaxCharSize > 1; + for (uint I=0;I<ASIZE(IsLeadByte);I++) + IsLeadByte[I]=IsDBCSLeadByte(I)!=0; +} + + +char* SupportDBCS::charnext(const char *s) +{ + // Zero cannot be the trail byte. So if next byte after the lead byte + // is 0, the string is corrupt and we'll better return the pointer to 0, + // to break string processing loops. + return (char *)(IsLeadByte[(byte)*s] && s[1]!=0 ? s+2:s+1); +} +#endif + + diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.hpp new file mode 100644 index 00000000..8d433c1b --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unicode.hpp @@ -0,0 +1,52 @@ +#ifndef _RAR_UNICODE_ +#define _RAR_UNICODE_ + +#if defined( _WIN_ALL) +#define DBCS_SUPPORTED +#endif + +bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); +bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); +byte* WideToRaw(const wchar *Src,byte *Dest,size_t SrcSize); +wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); +void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); +size_t WideToUtfSize(const wchar *Src); +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize); +bool IsTextUtf8(const byte *Src); +bool IsTextUtf8(const byte *Src,size_t SrcSize); + +int wcsicomp(const wchar *s1,const wchar *s2); +int wcsnicomp(const wchar *s1,const wchar *s2,size_t n); +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search); +#ifndef SFX_MODULE +wchar* wcslower(wchar *s); +wchar* wcsupper(wchar *s); +#endif +int toupperw(int ch); +int tolowerw(int ch); +int atoiw(const wchar *s); +int64 atoilw(const wchar *s); + +#ifdef DBCS_SUPPORTED +class SupportDBCS +{ + public: + SupportDBCS(); + void Init(); + char* charnext(const char *s); + + bool IsLeadByte[256]; + bool DBCSMode; +}; +extern SupportDBCS gdbcs; + +inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);} +inline bool IsDBCSMode() {return gdbcs.DBCSMode;} + +#else +#define charnext(s) ((s)+1) +#define IsDBCSMode() (false) +#endif + + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.cpp new file mode 100644 index 00000000..037c3554 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.cpp @@ -0,0 +1,365 @@ +#include "rar.hpp" + +#include "coder.cpp" +#include "suballoc.cpp" +#include "model.cpp" +#include "unpackinline.cpp" +#ifdef RAR_SMP +#include "unpack50mt.cpp" +#endif +#ifndef SFX_MODULE +#include "unpack15.cpp" +#include "unpack20.cpp" +#endif +#include "unpack30.cpp" +#include "unpack50.cpp" +#include "unpack50frag.cpp" + +Unpack::Unpack(ComprDataIO *DataIO) +:Inp(true),VMCodeInp(true) +{ + UnpIO=DataIO; + Window=NULL; + Fragmented=false; + Suspended=false; + UnpAllBuf=false; + UnpSomeRead=false; +#ifdef RAR_SMP + MaxUserThreads=1; + UnpThreadPool=NULL; + ReadBufMT=NULL; + UnpThreadData=NULL; +#endif + MaxWinSize=0; + MaxWinMask=0; + + // Perform initialization, which should be done only once for all files. + // It prevents crash if first DoUnpack call is later made with wrong + // (true) 'Solid' value. + UnpInitData(false); +#ifndef SFX_MODULE + // RAR 1.5 decompression initialization + UnpInitData15(false); + InitHuff(); +#endif +} + + +Unpack::~Unpack() +{ + InitFilters30(false); + + if (Window!=NULL) + free(Window); +#ifdef RAR_SMP + delete UnpThreadPool; + delete[] ReadBufMT; + delete[] UnpThreadData; +#endif +} + + +#ifdef RAR_SMP +void Unpack::SetThreads(uint Threads) +{ + // More than 8 threads are unlikely to provide noticeable gain + // for unpacking, but would use the additional memory. + MaxUserThreads=Min(Threads,8); + UnpThreadPool=new ThreadPool(MaxUserThreads); +} +#endif + + +void Unpack::Init(size_t WinSize,bool Solid) +{ + // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size + // will be 0 because of size_t overflow. Let's issue the memory error. + if (WinSize==0) + ErrHandler.MemoryError(); + + // Minimum window size must be at least twice more than maximum possible + // size of filter block, which is 0x10000 in RAR now. If window size is + // smaller, we can have a block with never cleared flt->NextWindow flag + // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's + // use 0x40000 for extra safety and possible filter area size expansion. + const size_t MinAllocSize=0x40000; + if (WinSize<MinAllocSize) + WinSize=MinAllocSize; + + if (WinSize<=MaxWinSize) // Use the already allocated window. + return; + if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB. + return; + + // Archiving code guarantees that window size does not grow in the same + // solid stream. So if we are here, we are either creating a new window + // or increasing the size of non-solid window. So we could safely reject + // current window data without copying them to a new window, though being + // extra cautious, we still handle the solid window grow case below. + bool Grow=Solid && (Window!=NULL || Fragmented); + + // We do not handle growth for existing fragmented window. + if (Grow && Fragmented) + throw std::bad_alloc(); + + byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize); + + if (NewWindow==NULL) + if (Grow || WinSize<0x1000000) + { + // We do not support growth for new fragmented window. + // Also exclude RAR4 and small dictionaries. + throw std::bad_alloc(); + } + else + { + if (Window!=NULL) // If allocated by preceding files. + { + free(Window); + Window=NULL; + } + FragWindow.Init(WinSize); + Fragmented=true; + } + + if (!Fragmented) + { + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access unused areas of sliding dictionary. + memset(NewWindow,0,WinSize); + + // If Window is not NULL, it means that window size has grown. + // In solid streams we need to copy data to a new window in such case. + // RAR archiving code does not allow it in solid streams now, + // but let's implement it anyway just in case we'll change it sometimes. + if (Grow) + for (size_t I=1;I<=MaxWinSize;I++) + NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; + + if (Window!=NULL) + free(Window); + Window=NewWindow; + } + + MaxWinSize=WinSize; + MaxWinMask=MaxWinSize-1; +} + + +void Unpack::DoUnpack(uint Method,bool Solid) +{ + // Methods <50 will crash in Fragmented mode when accessing NULL Window. + // They cannot be called in such mode now, but we check it below anyway + // just for extra safety. + switch(Method) + { +#ifndef SFX_MODULE + case 15: // rar 1.5 compression + if (!Fragmented) + Unpack15(Solid); + break; + case 20: // rar 2.x compression + case 26: // files larger than 2GB + if (!Fragmented) + Unpack20(Solid); + break; +#endif + case 29: // rar 3.x compression + if (!Fragmented) + Unpack29(Solid); + break; + case 50: // RAR 5.0 compression algorithm. +#ifdef RAR_SMP + if (MaxUserThreads>1) + { +// We do not use the multithreaded unpack routine to repack RAR archives +// in 'suspended' mode, because unlike the single threaded code it can +// write more than one dictionary for same loop pass. So we would need +// larger buffers of unknown size. Also we do not support multithreading +// in fragmented window mode. + if (!Fragmented) + { + Unpack5MT(Solid); + break; + } + } +#endif + Unpack5(Solid); + break; + } +} + + +void Unpack::UnpInitData(bool Solid) +{ + if (!Solid) + { + memset(OldDist,0,sizeof(OldDist)); + OldDistPtr=0; + LastDist=LastLength=0; +// memset(Window,0,MaxWinSize); + memset(&BlockTables,0,sizeof(BlockTables)); + UnpPtr=WrPtr=0; + WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; + } + // Filters never share several solid files, so we can safely reset them + // even in solid archive. + InitFilters(); + + Inp.InitBitInput(); + WrittenFileSize=0; + ReadTop=0; + ReadBorder=0; + + memset(&BlockHeader,0,sizeof(BlockHeader)); + BlockHeader.BlockSize=-1; // '-1' means not defined yet. +#ifndef SFX_MODULE + UnpInitData20(Solid); +#endif + UnpInitData30(Solid); + UnpInitData50(Solid); +} + + +// LengthTable contains the length in bits for every element of alphabet. +// Dec is the structure to decode Huffman code/ +// Size is size of length table and DecodeNum field in Dec structure, +void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) +{ + // Size of alphabet and DecodePos array. + Dec->MaxNum=Size; + + // Calculate how many entries for every bit length in LengthTable we have. + uint LengthCount[16]; + memset(LengthCount,0,sizeof(LengthCount)); + for (size_t I=0;I<Size;I++) + LengthCount[LengthTable[I] & 0xf]++; + + // We must not calculate the number of zero length codes. + LengthCount[0]=0; + + // Set the entire DecodeNum to zero. + memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + + // Initialize not really used entry for zero length code. + Dec->DecodePos[0]=0; + + // Start code for bit length 1 is 0. + Dec->DecodeLen[0]=0; + + // Right aligned upper limit code for current bit length. + uint UpperLimit=0; + + for (size_t I=1;I<16;I++) + { + // Adjust the upper limit code. + UpperLimit+=LengthCount[I]; + + // Left aligned upper limit code. + uint LeftAligned=UpperLimit<<(16-I); + + // Prepare the upper limit code for next bit length. + UpperLimit*=2; + + // Store the left aligned upper limit code. + Dec->DecodeLen[I]=(uint)LeftAligned; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1]; + } + + // Prepare the copy of DecodePos. We'll modify this copy below, + // so we cannot use the original DecodePos. + uint CopyDecodePos[ASIZE(Dec->DecodePos)]; + memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); + + // For every bit length in the bit length table and so for every item + // of alphabet. + for (uint I=0;I<Size;I++) + { + // Get the current bit length. + byte CurBitLength=LengthTable[I] & 0xf; + + if (CurBitLength!=0) + { + // Last position in code list for current bit length. + uint LastPos=CopyDecodePos[CurBitLength]; + + // Prepare the decode table, so this position in code list will be + // decoded to current alphabet item number. + Dec->DecodeNum[LastPos]=(ushort)I; + + // We'll use next position number for this bit length next time. + // So we pass through the entire range of positions available + // for every bit length. + CopyDecodePos[CurBitLength]++; + } + } + + // Define the number of bits to process in quick mode. We use more bits + // for larger alphabets. More bits means that more codes will be processed + // in quick mode, but also that more time will be spent to preparation + // of tables for quick decode. + switch (Size) + { + case NC: + case NC20: + case NC30: + Dec->QuickBits=MAX_QUICK_DECODE_BITS; + break; + default: + Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; + break; + } + + // Size of tables for quick mode. + uint QuickDataSize=1<<Dec->QuickBits; + + // Bit length for current code, start from 1 bit codes. It is important + // to use 1 bit instead of 0 for minimum code length, so we are moving + // forward even when processing a corrupt archive. + uint CurBitLength=1; + + // For every right aligned bit string which supports the quick decoding. + for (uint Code=0;Code<QuickDataSize;Code++) + { + // Left align the current code, so it will be in usual bit field format. + uint BitField=Code<<(16-Dec->QuickBits); + + // Prepare the table for quick decoding of bit lengths. + + // Find the upper limit for current bit field and adjust the bit length + // accordingly if necessary. + while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) + CurBitLength++; + + // Translation of right aligned bit string to bit length. + Dec->QuickLen[Code]=CurBitLength; + + // Prepare the table for quick translation of position in code list + // to position in alphabet. + + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[CurBitLength-1]; + + // Right align the distance. + Dist>>=(16-CurBitLength); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos; + if (CurBitLength<ASIZE(Dec->DecodePos) && + (Pos=Dec->DecodePos[CurBitLength]+Dist)<Size) + { + // Define the code to alphabet number translation. + Dec->QuickNum[Code]=Dec->DecodeNum[Pos]; + } + else + { + // Can be here for length table filled with zeroes only (empty). + Dec->QuickNum[Code]=0; + } + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.hpp new file mode 100644 index 00000000..30a9a2ee --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack.hpp @@ -0,0 +1,404 @@ +#ifndef _RAR_UNPACK_ +#define _RAR_UNPACK_ + +// Maximum allowed number of compressed bits processed in quick mode. +#define MAX_QUICK_DECODE_BITS 10 + +// Maximum number of filters per entire data block. Must be at least +// twice more than MAX_PACK_FILTERS to store filters from two data blocks. +#define MAX_UNPACK_FILTERS 8192 + +// Maximum number of filters per entire data block for RAR3 unpack. +// Must be at least twice more than v3_MAX_PACK_FILTERS to store filters +// from two data blocks. +#define MAX3_UNPACK_FILTERS 8192 + +// Limit maximum number of channels in RAR3 delta filter to some reasonable +// value to prevent too slow processing of corrupt archives with invalid +// channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. +// No need to provide it for RAR5, which uses only 5 bits to store channels. +#define MAX3_UNPACK_CHANNELS 1024 + +// Maximum size of single filter block. We restrict it to limit memory +// allocation. Must be equal or larger than MAX_ANALYZE_SIZE. +#define MAX_FILTER_BLOCK_SIZE 0x400000 + +// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_READ, +// so we keep the number of buffered filters in unpacker reasonable. +#define UNPACK_MAX_WRITE 0x400000 + +// Decode compressed bit fields to alphabet numbers. +struct DecodeTable:PackDef +{ + // Real size of DecodeNum table. + uint MaxNum; + + // Left aligned start and upper limit codes defining code space + // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of + // range for bit length and DecodeLen[BitLength] defines next code + // after the end of range or in other words the upper limit code + // for specified bit length. + uint DecodeLen[16]; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + uint DecodePos[16]; + + // Number of compressed bits processed in quick mode. + // Must not exceed MAX_QUICK_DECODE_BITS. + uint QuickBits; + + // Translates compressed bits (up to QuickBits length) + // to bit length in quick mode. + byte QuickLen[1<<MAX_QUICK_DECODE_BITS]; + + // Translates compressed bits (up to QuickBits length) + // to position in alphabet in quick mode. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + ushort QuickNum[1<<MAX_QUICK_DECODE_BITS]; + + // Translate the position in code list to position in alphabet. + // We do not allocate it dynamically to avoid performance overhead + // introduced by pointer, so we use the largest possible table size + // as array dimension. Real size of this array is defined in MaxNum. + // We use this array if compressed bit field is too lengthy + // for QuickLen based translation. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + ushort DecodeNum[LARGEST_TABLE_SIZE]; +}; + + +struct UnpackBlockHeader +{ + int BlockSize; + int BlockBitSize; + int BlockStart; + int HeaderSize; + bool LastBlockInFile; + bool TablePresent; +}; + + +struct UnpackBlockTables +{ + DecodeTable LD; // Decode literals. + DecodeTable DD; // Decode distances. + DecodeTable LDD; // Decode lower bits of distances. + DecodeTable RD; // Decode repeating distances. + DecodeTable BD; // Decode bit lengths in Huffman table. +}; + + +#ifdef RAR_SMP +enum UNP_DEC_TYPE { + UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER +}; + +struct UnpackDecodedItem +{ + UNP_DEC_TYPE Type; + ushort Length; + union + { + uint Distance; + byte Literal[4]; + }; +}; + + +struct UnpackThreadData +{ + Unpack *UnpackPtr; + BitInput Inp; + bool HeaderRead; + UnpackBlockHeader BlockHeader; + bool TableRead; + UnpackBlockTables BlockTables; + int DataSize; // Data left in buffer. Can be less than block size. + bool DamagedData; + bool LargeBlock; + bool NoDataLeft; // 'true' if file is read completely. + bool Incomplete; // Not entire block was processed, need to read more data. + + UnpackDecodedItem *Decoded; + uint DecodedSize; + uint DecodedAllocated; + uint ThreadNumber; // For debugging. + + UnpackThreadData() + :Inp(false) + { + Decoded=NULL; + } + ~UnpackThreadData() + { + if (Decoded!=NULL) + free(Decoded); + } +}; +#endif + + +struct UnpackFilter +{ + byte Type; + uint BlockStart; + uint BlockLength; + byte Channels; +// uint Width; +// byte PosR; + bool NextWindow; +}; + + +struct UnpackFilter30 +{ + unsigned int BlockStart; + unsigned int BlockLength; + bool NextWindow; + + // Position of parent filter in Filters array used as prototype for filter + // in PrgStack array. Not defined for filters in Filters array. + unsigned int ParentFilter; + + VM_PreparedProgram Prg; +}; + + +struct AudioVariables // For RAR 2.0 archives only. +{ + int K1,K2,K3,K4,K5; + int D1,D2,D3,D4; + int LastDelta; + unsigned int Dif[11]; + unsigned int ByteCount; + int LastChar; +}; + + +// We can use the fragmented dictionary in case heap does not have the single +// large enough memory block. It is slower than normal dictionary. +class FragmentedWindow +{ + private: + enum {MAX_MEM_BLOCKS=32}; + + void Reset(); + byte *Mem[MAX_MEM_BLOCKS]; + size_t MemSize[MAX_MEM_BLOCKS]; + public: + FragmentedWindow(); + ~FragmentedWindow(); + void Init(size_t WinSize); + byte& operator [](size_t Item); + void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask); + void CopyData(byte *Dest,size_t WinPos,size_t Size); + size_t GetBlockSize(size_t StartPos,size_t RequiredSize); +}; + + +class Unpack:PackDef +{ + private: + + void Unpack5(bool Solid); + void Unpack5MT(bool Solid); + bool UnpReadBuf(); + void UnpWriteBuf(); + byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt); + void UnpWriteArea(size_t StartPtr,size_t EndPtr); + void UnpWriteData(byte *Data,size_t Size); + _forceinline uint SlotToLength(BitInput &Inp,uint Slot); + void UnpInitData50(bool Solid); + bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header); + bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables); + void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size); + _forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec); + void CopyString(); + inline void InsertOldDist(unsigned int Distance); + void UnpInitData(bool Solid); + _forceinline void CopyString(uint Length,uint Distance); + uint ReadFilterData(BitInput &Inp); + bool ReadFilter(BitInput &Inp,UnpackFilter &Filter); + bool AddFilter(UnpackFilter &Filter); + bool AddFilter(); + void InitFilters(); + + ComprDataIO *UnpIO; + BitInput Inp; + +#ifdef RAR_SMP + void InitMT(); + bool UnpackLargeBlock(UnpackThreadData &D); + bool ProcessDecoded(UnpackThreadData &D); + + ThreadPool *UnpThreadPool; + UnpackThreadData *UnpThreadData; + uint MaxUserThreads; + byte *ReadBufMT; +#endif + + Array<byte> FilterSrcMemory; + Array<byte> FilterDstMemory; + + // Filters code, one entry per filter. + Array<UnpackFilter> Filters; + + uint OldDist[4],OldDistPtr; + uint LastLength; + + // LastDist is necessary only for RAR2 and older with circular OldDist + // array. In RAR3 last distance is always stored in OldDist[0]. + uint LastDist; + + size_t UnpPtr,WrPtr; + + // Top border of read packed data. + int ReadTop; + + // Border to call UnpReadBuf. We use it instead of (ReadTop-C) + // for optimization reasons. Ensures that we have C bytes in buffer + // unless we are at the end of file. + int ReadBorder; + + UnpackBlockHeader BlockHeader; + UnpackBlockTables BlockTables; + + size_t WriteBorder; + + byte *Window; + + FragmentedWindow FragWindow; + bool Fragmented; + + + int64 DestUnpSize; + + bool Suspended; + bool UnpAllBuf; + bool UnpSomeRead; + int64 WrittenFileSize; + bool FileExtracted; + + +/***************************** Unpack v 1.5 *********************************/ + void Unpack15(bool Solid); + void ShortLZ(); + void LongLZ(); + void HuffDecode(); + void GetFlagsBuf(); + void UnpInitData15(int Solid); + void InitHuff(); + void CorrHuff(ushort *CharSet,byte *NumToPlace); + void CopyString15(uint Distance,uint Length); + uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); + + ushort ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; + byte NToPl[256],NToPlB[256],NToPlC[256]; + uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; + int Buf60,NumHuf,StMode,LCount,FlagsCnt; + uint Nhfb,Nlzb,MaxDist3; +/***************************** Unpack v 1.5 *********************************/ + +/***************************** Unpack v 2.0 *********************************/ + void Unpack20(bool Solid); + + DecodeTable MD[4]; // Decode multimedia data, up to 4 channels. + + unsigned char UnpOldTable20[MC20*4]; + bool UnpAudioBlock; + uint UnpChannels,UnpCurChannel; + int UnpChannelDelta; + void CopyString20(uint Length,uint Distance); + bool ReadTables20(); + void UnpWriteBuf20(); + void UnpInitData20(int Solid); + void ReadLastTables(); + byte DecodeAudio(int Delta); + struct AudioVariables AudV[4]; +/***************************** Unpack v 2.0 *********************************/ + +/***************************** Unpack v 3.0 *********************************/ + enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; + + void UnpInitData30(bool Solid); + void Unpack29(bool Solid); + void InitFilters30(bool Solid); + bool ReadEndOfBlock(); + bool ReadVMCode(); + bool ReadVMCodePPM(); + bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize); + int SafePPMDecodeChar(); + bool ReadTables30(); + bool UnpReadBuf30(); + void UnpWriteBuf30(); + void ExecuteCode(VM_PreparedProgram *Prg); + + int PrevLowDist,LowDistRepCount; + + ModelPPM PPM; + int PPMEscChar; + + byte UnpOldTable[HUFF_TABLE_SIZE30]; + int UnpBlockType; + + // If we already read decoding tables for Unpack v2,v3,v5. + // We should not use a single variable for all algorithm versions, + // because we can have a corrupt archive with one algorithm file + // followed by another algorithm file with "solid" flag and we do not + // want to reuse tables from one algorithm in another. + bool TablesRead2,TablesRead3,TablesRead5; + + // Virtual machine to execute filters code. + RarVM VM; + + // Buffer to read VM filters code. We moved it here from AddVMCode + // function to reduce time spent in BitInput constructor. + BitInput VMCodeInp; + + // Filters code, one entry per filter. + Array<UnpackFilter30 *> Filters30; + + // Filters stack, several entrances of same filter are possible. + Array<UnpackFilter30 *> PrgStack; + + // Lengths of preceding data blocks, one length of one last block + // for every filter. Used to reduce the size required to write + // the data block length if lengths are repeating. + Array<int> OldFilterLengths; + + int LastFilter; +/***************************** Unpack v 3.0 *********************************/ + + public: + Unpack(ComprDataIO *DataIO); + ~Unpack(); + void Init(size_t WinSize,bool Solid); + void DoUnpack(uint Method,bool Solid); + bool IsFileExtracted() {return(FileExtracted);} + void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} + void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} + +#ifdef RAR_SMP + void SetThreads(uint Threads); + void UnpackDecode(UnpackThreadData &D); +#endif + + size_t MaxWinSize; + size_t MaxWinMask; + + uint GetChar() + { + if (Inp.InAddr>BitInput::MAX_SIZE-30) + { + UnpReadBuf(); + if (Inp.InAddr>=BitInput::MAX_SIZE) // If nothing was read. + return 0; + } + return Inp.InBuf[Inp.InAddr++]; + } +}; + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack15.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack15.cpp new file mode 100644 index 00000000..1e7cf76c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack15.cpp @@ -0,0 +1,489 @@ +#define STARTL1 2 +static unsigned int DecL1[]={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, + 0xee00,0xf000,0xf200,0xf200,0xffff}; +static unsigned int PosL1[]={0,0,0,2,3,5,7,11,16,20,24,32,32}; + +#define STARTL2 3 +static unsigned int DecL2[]={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, + 0xf000,0xf200,0xf240,0xffff}; +static unsigned int PosL2[]={0,0,0,0,5,7,9,13,18,22,26,34,36}; + +#define STARTHF0 4 +static unsigned int DecHf0[]={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, + 0xf200,0xf200,0xffff}; +static unsigned int PosHf0[]={0,0,0,0,0,8,16,24,33,33,33,33,33}; + + +#define STARTHF1 5 +static unsigned int DecHf1[]={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, + 0xf7e0,0xffff}; +static unsigned int PosHf1[]={0,0,0,0,0,0,4,44,60,76,80,80,127}; + + +#define STARTHF2 5 +static unsigned int DecHf2[]={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, + 0xffff,0xffff}; +static unsigned int PosHf2[]={0,0,0,0,0,0,2,7,53,117,233,0,0}; + + +#define STARTHF3 6 +static unsigned int DecHf3[]={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, + 0xffff}; +static unsigned int PosHf3[]={0,0,0,0,0,0,0,2,16,218,251,0,0}; + + +#define STARTHF4 8 +static unsigned int DecHf4[]={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; +static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; + + +void Unpack::Unpack15(bool Solid) +{ + UnpInitData(Solid); + UnpInitData15(Solid); + UnpReadBuf(); + if (!Solid) + { + InitHuff(); + UnpPtr=0; + } + else + UnpPtr=WrPtr; + --DestUnpSize; + if (DestUnpSize>=0) + { + GetFlagsBuf(); + FlagsCnt=8; + } + + while (DestUnpSize>=0) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>ReadTop-30 && !UnpReadBuf()) + break; + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) + UnpWriteBuf20(); + if (StMode) + { + HuffDecode(); + continue; + } + + if (--FlagsCnt < 0) + { + GetFlagsBuf(); + FlagsCnt=7; + } + + if (FlagBuf & 0x80) + { + FlagBuf<<=1; + if (Nlzb > Nhfb) + LongLZ(); + else + HuffDecode(); + } + else + { + FlagBuf<<=1; + if (--FlagsCnt < 0) + { + GetFlagsBuf(); + FlagsCnt=7; + } + if (FlagBuf & 0x80) + { + FlagBuf<<=1; + if (Nlzb > Nhfb) + HuffDecode(); + else + LongLZ(); + } + else + { + FlagBuf<<=1; + ShortLZ(); + } + } + } + UnpWriteBuf20(); +} + + +#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) +#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) + +void Unpack::ShortLZ() +{ + static unsigned int ShortLen1[]={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; + static unsigned int ShortXor1[]={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, + 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; + static unsigned int ShortLen2[]={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; + static unsigned int ShortXor2[]={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, + 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; + + + unsigned int Length,SaveLength; + unsigned int LastDistance; + unsigned int Distance; + int DistancePlace; + NumHuf=0; + + unsigned int BitField=Inp.fgetbits(); + if (LCount==2) + { + Inp.faddbits(1); + if (BitField >= 0x8000) + { + CopyString15((unsigned int)LastDist,LastLength); + return; + } + BitField <<= 1; + LCount=0; + } + + BitField>>=8; + +// not thread safe, replaced by GetShortLen1 and GetShortLen2 macro +// ShortLen1[1]=ShortLen2[3]=Buf60+3; + + if (AvrLn1<37) + { + for (Length=0;;Length++) + if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) + break; + Inp.faddbits(GetShortLen1(Length)); + } + else + { + for (Length=0;;Length++) + if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) + break; + Inp.faddbits(GetShortLen2(Length)); + } + + if (Length >= 9) + { + if (Length == 9) + { + LCount++; + CopyString15((unsigned int)LastDist,LastLength); + return; + } + if (Length == 14) + { + LCount=0; + Length=DecodeNum(Inp.fgetbits(),STARTL2,DecL2,PosL2)+5; + Distance=(Inp.fgetbits()>>1) | 0x8000; + Inp.faddbits(15); + LastLength=Length; + LastDist=Distance; + CopyString15(Distance,Length); + return; + } + + LCount=0; + SaveLength=Length; + Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; + Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2; + if (Length==0x101 && SaveLength==10) + { + Buf60 ^= 1; + return; + } + if (Distance > 256) + Length++; + if (Distance >= MaxDist3) + Length++; + + OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + CopyString15(Distance,Length); + return; + } + + LCount=0; + AvrLn1 += Length; + AvrLn1 -= AvrLn1 >> 4; + + DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; + Distance=ChSetA[DistancePlace]; + if (--DistancePlace != -1) + { + LastDistance=ChSetA[DistancePlace]; + ChSetA[DistancePlace+1]=LastDistance; + ChSetA[DistancePlace]=Distance; + } + Length+=2; + OldDist[OldDistPtr++] = ++Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + CopyString15(Distance,Length); +} + + +void Unpack::LongLZ() +{ + unsigned int Length; + unsigned int Distance; + unsigned int DistancePlace,NewDistancePlace; + unsigned int OldAvr2,OldAvr3; + + NumHuf=0; + Nlzb+=16; + if (Nlzb > 0xff) + { + Nlzb=0x90; + Nhfb >>= 1; + } + OldAvr2=AvrLn2; + + unsigned int BitField=Inp.fgetbits(); + if (AvrLn2 >= 122) + Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); + else + if (AvrLn2 >= 64) + Length=DecodeNum(BitField,STARTL1,DecL1,PosL1); + else + if (BitField < 0x100) + { + Length=BitField; + Inp.faddbits(16); + } + else + { + for (Length=0;((BitField<<Length)&0x8000)==0;Length++) + ; + Inp.faddbits(Length+1); + } + + AvrLn2 += Length; + AvrLn2 -= AvrLn2 >> 5; + + BitField=Inp.fgetbits(); + if (AvrPlcB > 0x28ff) + DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); + else + if (AvrPlcB > 0x6ff) + DistancePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); + else + DistancePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); + + AvrPlcB += DistancePlace; + AvrPlcB -= AvrPlcB >> 8; + while (1) + { + Distance = ChSetB[DistancePlace & 0xff]; + NewDistancePlace = NToPlB[Distance++ & 0xff]++; + if (!(Distance & 0xff)) + CorrHuff(ChSetB,NToPlB); + else + break; + } + + ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; + ChSetB[NewDistancePlace]=Distance; + + Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; + Inp.faddbits(7); + + OldAvr3=AvrLn3; + if (Length!=1 && Length!=4) + if (Length==0 && Distance <= MaxDist3) + { + AvrLn3++; + AvrLn3 -= AvrLn3 >> 8; + } + else + if (AvrLn3 > 0) + AvrLn3--; + Length+=3; + if (Distance >= MaxDist3) + Length++; + if (Distance <= 256) + Length+=8; + if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) + MaxDist3=0x7f00; + else + MaxDist3=0x2001; + OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; + LastLength=Length; + LastDist=Distance; + CopyString15(Distance,Length); +} + + +void Unpack::HuffDecode() +{ + unsigned int CurByte,NewBytePlace; + unsigned int Length; + unsigned int Distance; + int BytePlace; + + unsigned int BitField=Inp.fgetbits(); + + if (AvrPlc > 0x75ff) + BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); + else + if (AvrPlc > 0x5dff) + BytePlace=DecodeNum(BitField,STARTHF3,DecHf3,PosHf3); + else + if (AvrPlc > 0x35ff) + BytePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); + else + if (AvrPlc > 0x0dff) + BytePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); + else + BytePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); + BytePlace&=0xff; + if (StMode) + { + if (BytePlace==0 && BitField > 0xfff) + BytePlace=0x100; + if (--BytePlace==-1) + { + BitField=Inp.fgetbits(); + Inp.faddbits(1); + if (BitField & 0x8000) + { + NumHuf=StMode=0; + return; + } + else + { + Length = (BitField & 0x4000) ? 4 : 3; + Inp.faddbits(1); + Distance=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + Distance = (Distance << 5) | (Inp.fgetbits() >> 11); + Inp.faddbits(5); + CopyString15(Distance,Length); + return; + } + } + } + else + if (NumHuf++ >= 16 && FlagsCnt==0) + StMode=1; + AvrPlc += BytePlace; + AvrPlc -= AvrPlc >> 8; + Nhfb+=16; + if (Nhfb > 0xff) + { + Nhfb=0x90; + Nlzb >>= 1; + } + + Window[UnpPtr++]=(byte)(ChSet[BytePlace]>>8); + --DestUnpSize; + + while (1) + { + CurByte=ChSet[BytePlace]; + NewBytePlace=NToPl[CurByte++ & 0xff]++; + if ((CurByte & 0xff) > 0xa1) + CorrHuff(ChSet,NToPl); + else + break; + } + + ChSet[BytePlace]=ChSet[NewBytePlace]; + ChSet[NewBytePlace]=CurByte; +} + + +void Unpack::GetFlagsBuf() +{ + unsigned int Flags,NewFlagsPlace; + unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + + // Our Huffman table stores 257 items and needs all them in other parts + // of code such as when StMode is on, so the first item is control item. + // While normally we do not use the last item to code the flags byte here, + // we need to check for value 256 when unpacking in case we unpack + // a corrupt archive. + if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0])) + return; + + while (1) + { + Flags=ChSetC[FlagsPlace]; + FlagBuf=Flags>>8; + NewFlagsPlace=NToPlC[Flags++ & 0xff]++; + if ((Flags & 0xff) != 0) + break; + CorrHuff(ChSetC,NToPlC); + } + + ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace]; + ChSetC[NewFlagsPlace]=Flags; +} + + +void Unpack::UnpInitData15(int Solid) +{ + if (!Solid) + { + AvrPlcB=AvrLn1=AvrLn2=AvrLn3=NumHuf=Buf60=0; + AvrPlc=0x3500; + MaxDist3=0x2001; + Nhfb=Nlzb=0x80; + } + FlagsCnt=0; + FlagBuf=0; + StMode=0; + LCount=0; + ReadTop=0; +} + + +void Unpack::InitHuff() +{ + for (unsigned int I=0;I<256;I++) + { + ChSet[I]=ChSetB[I]=I<<8; + ChSetA[I]=I; + ChSetC[I]=((~I+1) & 0xff)<<8; + } + memset(NToPl,0,sizeof(NToPl)); + memset(NToPlB,0,sizeof(NToPlB)); + memset(NToPlC,0,sizeof(NToPlC)); + CorrHuff(ChSetB,NToPlB); +} + + +void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) +{ + int I,J; + for (I=7;I>=0;I--) + for (J=0;J<32;J++,CharSet++) + *CharSet=(*CharSet & ~0xff) | I; + memset(NumToPlace,0,sizeof(NToPl)); + for (I=6;I>=0;I--) + NumToPlace[I]=(7-I)*32; +} + + +void Unpack::CopyString15(uint Distance,uint Length) +{ + DestUnpSize-=Length; + while (Length--) + { + Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +uint Unpack::DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) +{ + int I; + for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) + StartPos++; + Inp.faddbits(StartPos); + return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack20.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack20.cpp new file mode 100644 index 00000000..93c8ba05 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack20.cpp @@ -0,0 +1,379 @@ +#include "rar.hpp" + +void Unpack::CopyString20(uint Length,uint Distance) +{ + LastDist=OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0. + LastLength=Length; + DestUnpSize-=Length; + CopyString(Length,Distance); +} + + +void Unpack::Unpack20(bool Solid) +{ + static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; + static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; + static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; + static unsigned char DBits[]= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; + static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; + uint Bits; + + if (Suspended) + UnpPtr=WrPtr; + else + { + UnpInitData(Solid); + if (!UnpReadBuf()) + return; + if ((!Solid || !TablesRead2) && !ReadTables20()) + return; + --DestUnpSize; + } + + while (DestUnpSize>=0) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>ReadTop-30) + if (!UnpReadBuf()) + break; + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) + { + UnpWriteBuf20(); + if (Suspended) + return; + } + if (UnpAudioBlock) + { + uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]); + + if (AudioNumber==256) + { + if (!ReadTables20()) + break; + continue; + } + Window[UnpPtr++]=DecodeAudio((int)AudioNumber); + if (++UnpCurChannel==UnpChannels) + UnpCurChannel=0; + --DestUnpSize; + continue; + } + + uint Number=DecodeNumber(Inp,&BlockTables.LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + --DestUnpSize; + continue; + } + if (Number>269) + { + uint Length=LDecode[Number-=270]+3; + if ((Bits=LBits[Number])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000L) + Length++; + } + + CopyString20(Length,Distance); + continue; + } + if (Number==269) + { + if (!ReadTables20()) + break; + continue; + } + if (Number==256) + { + CopyString20(LastLength,LastDist); + continue; + } + if (Number<261) + { + uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + uint Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + if (Distance>=0x101) + { + Length++; + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000) + Length++; + } + } + CopyString20(Length,Distance); + continue; + } + if (Number<270) + { + uint Distance=SDDecode[Number-=261]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + CopyString20(2,Distance); + continue; + } + } + ReadLastTables(); + UnpWriteBuf20(); +} + + +void Unpack::UnpWriteBuf20() +{ + if (UnpPtr!=WrPtr) + UnpSomeRead=true; + if (UnpPtr<WrPtr) + { + UnpIO->UnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); + UnpIO->UnpWrite(Window,UnpPtr); + UnpAllBuf=true; + } + else + UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); + WrPtr=UnpPtr; +} + + +bool Unpack::ReadTables20() +{ + byte BitLength[BC20]; + byte Table[MC20*4]; + if (Inp.InAddr>ReadTop-25) + if (!UnpReadBuf()) + return false; + uint BitField=Inp.getbits(); + UnpAudioBlock=(BitField & 0x8000)!=0; + + if (!(BitField & 0x4000)) + memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + Inp.addbits(2); + + uint TableSize; + if (UnpAudioBlock) + { + UnpChannels=((BitField>>12) & 3)+1; + if (UnpCurChannel>=UnpChannels) + UnpCurChannel=0; + Inp.addbits(2); + TableSize=MC20*UnpChannels; + } + else + TableSize=NC20+DC20+RC20; + + for (uint I=0;I<BC20;I++) + { + BitLength[I]=(byte)(Inp.getbits() >> 12); + Inp.addbits(4); + } + MakeDecodeTables(BitLength,&BlockTables.BD,BC20); + for (uint I=0;I<TableSize;) + { + if (Inp.InAddr>ReadTop-5) + if (!UnpReadBuf()) + return false; + uint Number=DecodeNumber(Inp,&BlockTables.BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable20[I]) & 0xf; + I++; + } + else + if (Number==16) + { + uint N=(Inp.getbits() >> 14)+3; + Inp.addbits(2); + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } + } + else + { + uint N; + if (Number==17) + { + N=(Inp.getbits() >> 13)+3; + Inp.addbits(3); + } + else + { + N=(Inp.getbits() >> 9)+11; + Inp.addbits(7); + } + while (N-- > 0 && I<TableSize) + Table[I++]=0; + } + } + TablesRead2=true; + if (Inp.InAddr>ReadTop) + return true; + if (UnpAudioBlock) + for (uint I=0;I<UnpChannels;I++) + MakeDecodeTables(&Table[I*MC20],&MD[I],MC20); + else + { + MakeDecodeTables(&Table[0],&BlockTables.LD,NC20); + MakeDecodeTables(&Table[NC20],&BlockTables.DD,DC20); + MakeDecodeTables(&Table[NC20+DC20],&BlockTables.RD,RC20); + } + memcpy(UnpOldTable20,Table,TableSize); + return true; +} + + +void Unpack::ReadLastTables() +{ + if (ReadTop>=Inp.InAddr+5) + if (UnpAudioBlock) + { + if (DecodeNumber(Inp,&MD[UnpCurChannel])==256) + ReadTables20(); + } + else + if (DecodeNumber(Inp,&BlockTables.LD)==269) + ReadTables20(); +} + + +void Unpack::UnpInitData20(int Solid) +{ + if (!Solid) + { + TablesRead2=false; + UnpAudioBlock=false; + UnpChannelDelta=0; + UnpCurChannel=0; + UnpChannels=1; + + memset(AudV,0,sizeof(AudV)); + memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + memset(MD,0,sizeof(MD)); + } +} + + +byte Unpack::DecodeAudio(int Delta) +{ + struct AudioVariables *V=&AudV[UnpCurChannel]; + V->ByteCount++; + V->D4=V->D3; + V->D3=V->D2; + V->D2=V->LastDelta-V->D1; + V->D1=V->LastDelta; + int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; + PCh=(PCh>>3) & 0xFF; + + uint Ch=PCh-Delta; + + int D=(signed char)Delta; + // Left shift of negative value is undefined behavior in C++, + // so we cast it to unsigned to follow the standard. + D=(uint)D<<3; + + V->Dif[0]+=abs(D); + V->Dif[1]+=abs(D-V->D1); + V->Dif[2]+=abs(D+V->D1); + V->Dif[3]+=abs(D-V->D2); + V->Dif[4]+=abs(D+V->D2); + V->Dif[5]+=abs(D-V->D3); + V->Dif[6]+=abs(D+V->D3); + V->Dif[7]+=abs(D-V->D4); + V->Dif[8]+=abs(D+V->D4); + V->Dif[9]+=abs(D-UnpChannelDelta); + V->Dif[10]+=abs(D+UnpChannelDelta); + + UnpChannelDelta=V->LastDelta=(signed char)(Ch-V->LastChar); + V->LastChar=Ch; + + if ((V->ByteCount & 0x1F)==0) + { + uint MinDif=V->Dif[0],NumMinDif=0; + V->Dif[0]=0; + for (uint I=1;I<ASIZE(V->Dif);I++) + { + if (V->Dif[I]<MinDif) + { + MinDif=V->Dif[I]; + NumMinDif=I; + } + V->Dif[I]=0; + } + switch(NumMinDif) + { + case 1: + if (V->K1>=-16) + V->K1--; + break; + case 2: + if (V->K1<16) + V->K1++; + break; + case 3: + if (V->K2>=-16) + V->K2--; + break; + case 4: + if (V->K2<16) + V->K2++; + break; + case 5: + if (V->K3>=-16) + V->K3--; + break; + case 6: + if (V->K3<16) + V->K3++; + break; + case 7: + if (V->K4>=-16) + V->K4--; + break; + case 8: + if (V->K4<16) + V->K4++; + break; + case 9: + if (V->K5>=-16) + V->K5--; + break; + case 10: + if (V->K5<16) + V->K5++; + break; + } + } + return (byte)Ch; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack30.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack30.cpp new file mode 100644 index 00000000..6a8efa23 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack30.cpp @@ -0,0 +1,765 @@ +// We use it instead of direct PPM.DecodeChar call to be sure that +// we reset PPM structures in case of corrupt data. It is important, +// because these structures can be invalid after PPM.DecodeChar returned -1. +inline int Unpack::SafePPMDecodeChar() +{ + int Ch=PPM.DecodeChar(); + if (Ch==-1) // Corrupt PPM data found. + { + PPM.CleanUp(); // Reset possibly corrupt PPM data structures. + UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. + } + return(Ch); +} + + +void Unpack::Unpack29(bool Solid) +{ + static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; + static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; + static int DDecode[DC]; + static byte DBits[DC]; + static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; + static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; + static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; + unsigned int Bits; + + if (DDecode[1]==0) + { + int Dist=0,BitLength=0,Slot=0; + for (int I=0;I<ASIZE(DBitLengthCounts);I++,BitLength++) + for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength)) + { + DDecode[Slot]=Dist; + DBits[Slot]=BitLength; + } + } + + FileExtracted=true; + + if (!Suspended) + { + UnpInitData(Solid); + if (!UnpReadBuf30()) + return; + if ((!Solid || !TablesRead3) && !ReadTables30()) + return; + } + + while (true) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>ReadBorder) + { + if (!UnpReadBuf30()) + break; + } + if (((WrPtr-UnpPtr) & MaxWinMask)<260 && WrPtr!=UnpPtr) + { + UnpWriteBuf30(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + if (UnpBlockType==BLOCK_PPM) + { + // Here speed is critical, so we do not use SafePPMDecodeChar, + // because sometimes even the inline function can introduce + // some additional penalty. + int Ch=PPM.DecodeChar(); + if (Ch==-1) // Corrupt PPM data found. + { + PPM.CleanUp(); // Reset possibly corrupt PPM data structures. + UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. + break; + } + if (Ch==PPMEscChar) + { + int NextCh=SafePPMDecodeChar(); + if (NextCh==0) // End of PPM encoding. + { + if (!ReadTables30()) + break; + continue; + } + if (NextCh==-1) // Corrupt PPM data found. + break; + if (NextCh==2) // End of file in PPM mode. + break; + if (NextCh==3) // Read VM code. + { + if (!ReadVMCodePPM()) + break; + continue; + } + if (NextCh==4) // LZ inside of PPM. + { + unsigned int Distance=0,Length; + bool Failed=false; + for (int I=0;I<4 && !Failed;I++) + { + int Ch=SafePPMDecodeChar(); + if (Ch==-1) + Failed=true; + else + if (I==3) + Length=(byte)Ch; + else + Distance=(Distance<<8)+(byte)Ch; + } + if (Failed) + break; + + CopyString(Length+32,Distance+2); + continue; + } + if (NextCh==5) // One byte distance match (RLE) inside of PPM. + { + int Length=SafePPMDecodeChar(); + if (Length==-1) + break; + CopyString(Length+4,1); + continue; + } + // If we are here, NextCh must be 1, what means that current byte + // is equal to our 'escape' byte, so we just store it to Window. + } + Window[UnpPtr++]=Ch; + continue; + } + + uint Number=DecodeNumber(Inp,&BlockTables.LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + continue; + } + if (Number>=271) + { + uint Length=LDecode[Number-=271]+3; + if ((Bits=LBits[Number])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + if (DistNumber>9) + { + if (Bits>4) + { + Distance+=((Inp.getbits()>>(20-Bits))<<4); + Inp.addbits(Bits-4); + } + if (LowDistRepCount>0) + { + LowDistRepCount--; + Distance+=PrevLowDist; + } + else + { + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + if (LowDist==16) + { + LowDistRepCount=LOW_DIST_REP_COUNT-1; + Distance+=PrevLowDist; + } + else + { + Distance+=LowDist; + PrevLowDist=LowDist; + } + } + } + else + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000) + Length++; + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number==256) + { + if (!ReadEndOfBlock()) + break; + continue; + } + if (Number==257) + { + if (!ReadVMCode()) + break; + continue; + } + if (Number==258) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (Number<263) + { + uint DistNum=Number-259; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + int Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number<272) + { + uint Distance=SDDecode[Number-=263]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + InsertOldDist(Distance); + LastLength=2; + CopyString(2,Distance); + continue; + } + } + UnpWriteBuf30(); +} + + +// Return 'false' to quit unpacking the current file or 'true' to continue. +bool Unpack::ReadEndOfBlock() +{ + uint BitField=Inp.getbits(); + bool NewTable,NewFile=false; + + // "1" - no new file, new table just here. + // "00" - new file, no new table. + // "01" - new file, new table (in beginning of next file). + + if ((BitField & 0x8000)!=0) + { + NewTable=true; + Inp.addbits(1); + } + else + { + NewFile=true; + NewTable=(BitField & 0x4000)!=0; + Inp.addbits(2); + } + TablesRead3=!NewTable; + + // Quit immediately if "new file" flag is set. If "new table" flag + // is present, we'll read the table in beginning of next file + // based on 'TablesRead3' 'false' value. + if (NewFile) + return false; + return ReadTables30(); // Quit only if we failed to read tables. +} + + +bool Unpack::ReadVMCode() +{ + // Entire VM code is guaranteed to fully present in block defined + // by current Huffman table. Compressor checks that VM code does not cross + // Huffman block boundaries. + uint FirstByte=Inp.getbits()>>8; + Inp.addbits(8); + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + Length=(Inp.getbits()>>8)+7; + Inp.addbits(8); + } + else + if (Length==8) + { + Length=Inp.getbits(); + Inp.addbits(16); + } + if (Length==0) + return false; + Array<byte> VMCode(Length); + for (uint I=0;I<Length;I++) + { + // Try to read the new buffer if only one byte is left. + // But if we read all bytes except the last, one byte is enough. + if (Inp.InAddr>=ReadTop-1 && !UnpReadBuf30() && I<Length-1) + return false; + VMCode[I]=Inp.getbits()>>8; + Inp.addbits(8); + } + return AddVMCode(FirstByte,&VMCode[0],Length); +} + + +bool Unpack::ReadVMCodePPM() +{ + uint FirstByte=SafePPMDecodeChar(); + if ((int)FirstByte==-1) + return false; + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + Length=B1+7; + } + else + if (Length==8) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + int B2=SafePPMDecodeChar(); + if (B2==-1) + return false; + Length=B1*256+B2; + } + if (Length==0) + return false; + Array<byte> VMCode(Length); + for (uint I=0;I<Length;I++) + { + int Ch=SafePPMDecodeChar(); + if (Ch==-1) + return false; + VMCode[I]=Ch; + } + return AddVMCode(FirstByte,&VMCode[0],Length); +} + + +bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) +{ + VMCodeInp.InitBitInput(); + memcpy(VMCodeInp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); + VM.Init(); + + uint FiltPos; + if ((FirstByte & 0x80)!=0) + { + FiltPos=RarVM::ReadData(VMCodeInp); + if (FiltPos==0) + InitFilters30(false); + else + FiltPos--; + } + else + FiltPos=LastFilter; // Use the same filter as last time. + + if (FiltPos>Filters30.Size() || FiltPos>OldFilterLengths.Size()) + return false; + LastFilter=FiltPos; + bool NewFilter=(FiltPos==Filters30.Size()); + + UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. + + UnpackFilter30 *Filter; + if (NewFilter) // New filter code, never used before since VM reset. + { + if (FiltPos>MAX3_UNPACK_FILTERS) + { + // Too many different filters, corrupt archive. + delete StackFilter; + return false; + } + + Filters30.Add(1); + Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; + StackFilter->ParentFilter=(uint)(Filters30.Size()-1); + + // Reserve one item to store the data block length of our new filter + // entry. We'll set it to real block length below, after reading it. + // But we need to initialize it now, because when processing corrupt + // data, we can access this item even before we set it to real value. + OldFilterLengths.Push(0); + } + else // Filter was used in the past. + { + Filter=Filters30[FiltPos]; + StackFilter->ParentFilter=FiltPos; + } + + uint EmptyCount=0; + for (uint I=0;I<PrgStack.Size();I++) + { + PrgStack[I-EmptyCount]=PrgStack[I]; + if (PrgStack[I]==NULL) + EmptyCount++; + if (EmptyCount>0) + PrgStack[I]=NULL; + } + if (EmptyCount==0) + { + if (PrgStack.Size()>MAX3_UNPACK_FILTERS) + { + delete StackFilter; + return false; + } + PrgStack.Add(1); + EmptyCount=1; + } + size_t StackPos=PrgStack.Size()-EmptyCount; + PrgStack[StackPos]=StackFilter; + + uint BlockStart=RarVM::ReadData(VMCodeInp); + if ((FirstByte & 0x40)!=0) + BlockStart+=258; + StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); + if ((FirstByte & 0x20)!=0) + { + StackFilter->BlockLength=RarVM::ReadData(VMCodeInp); + + // Store the last data block length for current filter. + OldFilterLengths[FiltPos]=StackFilter->BlockLength; + } + else + { + // Set the data block size to same value as the previous block size + // for same filter. It is possible for corrupt data to access a new + // and not filled yet item of OldFilterLengths array here. This is why + // we set new OldFilterLengths items to zero above. + StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0; + } + + StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; + +// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + + memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); + StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + + if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. + { + uint InitMask=VMCodeInp.fgetbits()>>9; + VMCodeInp.faddbits(7); + for (uint I=0;I<7;I++) + if (InitMask & (1<<I)) + StackFilter->Prg.InitR[I]=RarVM::ReadData(VMCodeInp); + } + + if (NewFilter) + { + uint VMCodeSize=RarVM::ReadData(VMCodeInp); + if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) + return false; + Array<byte> VMCode(VMCodeSize); + for (uint I=0;I<VMCodeSize;I++) + { + if (VMCodeInp.Overflow(3)) + return false; + VMCode[I]=VMCodeInp.fgetbits()>>8; + VMCodeInp.faddbits(8); + } + VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + } + StackFilter->Prg.Type=Filter->Prg.Type; + + return true; +} + + +bool Unpack::UnpReadBuf30() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf30() +{ + uint WrittenBorder=(uint)WrPtr; + uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); + for (size_t I=0;I<PrgStack.Size();I++) + { + // Here we apply filters to data which we need to write. + // We always copy data to virtual machine memory before processing. + // We cannot process them just in place in Window buffer, because + // these data can be used for future string matches, so we must + // preserve them in original form. + + UnpackFilter30 *flt=PrgStack[I]; + if (flt==NULL) + continue; + if (flt->NextWindow) + { + flt->NextWindow=false; + continue; + } + unsigned int BlockStart=flt->BlockStart; + unsigned int BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSize) + { + if (WrittenBorder!=BlockStart) + { + UnpWriteArea(WrittenBorder,BlockStart); + WrittenBorder=BlockStart; + WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); + } + if (BlockLength<=WriteSize) + { + uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + if (BlockStart<BlockEnd || BlockEnd==0) + VM.SetMemory(0,Window+BlockStart,BlockLength); + else + { + uint FirstPartLength=uint(MaxWinSize-BlockStart); + VM.SetMemory(0,Window+BlockStart,FirstPartLength); + VM.SetMemory(FirstPartLength,Window,BlockEnd); + } + + VM_PreparedProgram *ParentPrg=&Filters30[flt->ParentFilter]->Prg; + VM_PreparedProgram *Prg=&flt->Prg; + + ExecuteCode(Prg); + + byte *FilteredData=Prg->FilteredData; + unsigned int FilteredDataSize=Prg->FilteredDataSize; + + delete PrgStack[I]; + PrgStack[I]=NULL; + while (I+1<PrgStack.Size()) + { + UnpackFilter30 *NextFilter=PrgStack[I+1]; + // It is required to check NextWindow here. + if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart || + NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) + break; + + // Apply several filters to same data block. + + VM.SetMemory(0,FilteredData,FilteredDataSize); + + VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; + VM_PreparedProgram *NextPrg=&NextFilter->Prg; + + ExecuteCode(NextPrg); + + FilteredData=NextPrg->FilteredData; + FilteredDataSize=NextPrg->FilteredDataSize; + I++; + delete PrgStack[I]; + PrgStack[I]=NULL; + } + UnpIO->UnpWrite(FilteredData,FilteredDataSize); + UnpSomeRead=true; + WrittenFileSize+=FilteredDataSize; + WrittenBorder=BlockEnd; + WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask); + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + for (size_t J=I;J<PrgStack.Size();J++) + { + UnpackFilter30 *flt=PrgStack[J]; + if (flt!=NULL && flt->NextWindow) + flt->NextWindow=false; + } + WrPtr=WrittenBorder; + return; + } + } + } + + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; +} + + +void Unpack::ExecuteCode(VM_PreparedProgram *Prg) +{ + Prg->InitR[6]=(uint)WrittenFileSize; + VM.Execute(Prg); +} + + +bool Unpack::ReadTables30() +{ + byte BitLength[BC]; + byte Table[HUFF_TABLE_SIZE30]; + if (Inp.InAddr>ReadTop-25) + if (!UnpReadBuf30()) + return(false); + Inp.faddbits((8-Inp.InBit)&7); + uint BitField=Inp.fgetbits(); + if (BitField & 0x8000) + { + UnpBlockType=BLOCK_PPM; + return(PPM.DecodeInit(this,PPMEscChar)); + } + UnpBlockType=BLOCK_LZ; + + PrevLowDist=0; + LowDistRepCount=0; + + if (!(BitField & 0x4000)) + memset(UnpOldTable,0,sizeof(UnpOldTable)); + Inp.faddbits(2); + + for (uint I=0;I<BC;I++) + { + uint Length=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && I<ASIZE(BitLength)) + BitLength[I++]=0; + I--; + } + } + else + BitLength[I]=Length; + } + MakeDecodeTables(BitLength,&BlockTables.BD,BC30); + + const uint TableSize=HUFF_TABLE_SIZE30; + for (uint I=0;I<TableSize;) + { + if (Inp.InAddr>ReadTop-5) + if (!UnpReadBuf30()) + return(false); + uint Number=DecodeNumber(Inp,&BlockTables.BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable[I]) & 0xf; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } + } + else + { + uint N; + if (Number==18) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && I<TableSize) + Table[I++]=0; + } + } + TablesRead3=true; + if (Inp.InAddr>ReadTop) + return false; + MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); + MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); + MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); + MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); + memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + return true; +} + + +void Unpack::UnpInitData30(bool Solid) +{ + if (!Solid) + { + TablesRead3=false; + memset(UnpOldTable,0,sizeof(UnpOldTable)); + PPMEscChar=2; + UnpBlockType=BLOCK_LZ; + } + InitFilters30(Solid); +} + + +void Unpack::InitFilters30(bool Solid) +{ + if (!Solid) + { + OldFilterLengths.SoftReset(); + LastFilter=0; + + for (size_t I=0;I<Filters30.Size();I++) + delete Filters30[I]; + Filters30.SoftReset(); + } + for (size_t I=0;I<PrgStack.Size();I++) + delete PrgStack[I]; + PrgStack.SoftReset(); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50.cpp new file mode 100644 index 00000000..99119507 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50.cpp @@ -0,0 +1,687 @@ +void Unpack::Unpack5(bool Solid) +{ + FileExtracted=true; + + if (!Suspended) + { + UnpInitData(Solid); + if (!UnpReadBuf()) + return; + + // Check TablesRead5 to be sure that we read tables at least once + // regardless of current block header TablePresent flag. + // So we can safefly use these tables below. + if (!ReadBlockHeader(Inp,BlockHeader) || + !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5) + return; + } + + while (true) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>=ReadBorder) + { + bool FileDone=false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || + Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && + Inp.InBit>=BlockHeader.BlockBitSize) + { + if (BlockHeader.LastBlockInFile) + { + FileDone=true; + break; + } + if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) + return; + } + if (FileDone || !UnpReadBuf()) + break; + } + + if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + + uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); + if (MainSlot<256) + { + if (Fragmented) + FragWindow[UnpPtr++]=(byte)MainSlot; + else + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((Inp.getbits32()>>(36-DBits))<<4); + Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=Inp.getbits32()>>(32-DBits); + Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + if (Fragmented) + FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); + else + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); + uint Length=SlotToLength(Inp,LengthSlot); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + } + UnpWriteBuf(); +} + + +uint Unpack::ReadFilterData(BitInput &Inp) +{ + uint ByteCount=(Inp.fgetbits()>>14)+1; + Inp.addbits(2); + + uint Data=0; + for (uint I=0;I<ByteCount;I++) + { + Data+=(Inp.fgetbits()>>8)<<(I*8); + Inp.addbits(8); + } + return Data; +} + + +bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter) +{ + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) + if (!UnpReadBuf()) + return false; + + Filter.BlockStart=ReadFilterData(Inp); + Filter.BlockLength=ReadFilterData(Inp); + if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) + Filter.BlockLength=0; + + Filter.Type=Inp.fgetbits()>>13; + Inp.faddbits(3); + + if (Filter.Type==FILTER_DELTA) + { + Filter.Channels=(Inp.fgetbits()>>11)+1; + Inp.faddbits(5); + } + + return true; +} + + +bool Unpack::AddFilter(UnpackFilter &Filter) +{ + if (Filters.Size()>=MAX_UNPACK_FILTERS) + { + UnpWriteBuf(); // Write data, apply and flush filters. + if (Filters.Size()>=MAX_UNPACK_FILTERS) + InitFilters(); // Still too many filters, prevent excessive memory use. + } + + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; + + Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); + Filters.Push(Filter); + return true; +} + + +bool Unpack::UnpReadBuf() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=0; + if (BitInput::MAX_SIZE!=DataSize) + ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) // Can be also -1. + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + BlockHeader.BlockStart=Inp.InAddr; + if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); + } + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf() +{ + size_t WrittenBorder=WrPtr; + size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; + size_t WriteSizeLeft=FullWriteSize; + bool NotAllFiltersProcessed=false; + for (size_t I=0;I<Filters.Size();I++) + { + // Here we apply filters to data which we need to write. + // We always copy data to another memory block before processing. + // We cannot process them just in place in Window buffer, because + // these data can be used for future string matches, so we must + // preserve them in original form. + + UnpackFilter *flt=&Filters[I]; + if (flt->Type==FILTER_NONE) + continue; + if (flt->NextWindow) + { + // Here we skip filters which have block start in current data range + // due to address wrap around in circular dictionary, but actually + // belong to next dictionary block. If such filter start position + // is included to current write range, then we reset 'NextWindow' flag. + // In fact we can reset it even without such check, because current + // implementation seems to guarantee 'NextWindow' flag reset after + // buffer writing for all existing filters. But let's keep this check + // just in case. Compressor guarantees that distance between + // filter block start and filter storing position cannot exceed + // the dictionary size. So if we covered the filter block start with + // our write here, we can safely assume that filter is applicable + // to next block on no further wrap arounds is possible. + if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) + flt->NextWindow=false; + continue; + } + uint BlockStart=flt->BlockStart; + uint BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSizeLeft) + { + if (WrittenBorder!=BlockStart) + { + UnpWriteArea(WrittenBorder,BlockStart); + WrittenBorder=BlockStart; + WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + } + if (BlockLength<=WriteSizeLeft) + { + if (BlockLength>0) // We set it to 0 also for invalid filters. + { + uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + + FilterSrcMemory.Alloc(BlockLength); + byte *Mem=&FilterSrcMemory[0]; + if (BlockStart<BlockEnd || BlockEnd==0) + { + if (Fragmented) + FragWindow.CopyData(Mem,BlockStart,BlockLength); + else + memcpy(Mem,Window+BlockStart,BlockLength); + } + else + { + size_t FirstPartLength=size_t(MaxWinSize-BlockStart); + if (Fragmented) + { + FragWindow.CopyData(Mem,BlockStart,FirstPartLength); + FragWindow.CopyData(Mem+FirstPartLength,0,BlockEnd); + } + else + { + memcpy(Mem,Window+BlockStart,FirstPartLength); + memcpy(Mem+FirstPartLength,Window,BlockEnd); + } + } + + byte *OutMem=ApplyFilter(Mem,BlockLength,flt); + + Filters[I].Type=FILTER_NONE; + + if (OutMem!=NULL) + UnpIO->UnpWrite(OutMem,BlockLength); + + UnpSomeRead=true; + WrittenFileSize+=BlockLength; + WrittenBorder=BlockEnd; + WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + } + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + WrPtr=WrittenBorder; + + // Since Filter start position can only increase, we quit processing + // all following filters for this data block and reset 'NextWindow' + // flag for them. + for (size_t J=I;J<Filters.Size();J++) + { + UnpackFilter *flt=&Filters[J]; + if (flt->Type!=FILTER_NONE) + flt->NextWindow=false; + } + + // Do not write data left after current filter now. + NotAllFiltersProcessed=true; + break; + } + } + } + + // Remove processed filters from queue. + size_t EmptyCount=0; + for (size_t I=0;I<Filters.Size();I++) + { + if (EmptyCount>0) + Filters[I-EmptyCount]=Filters[I]; + if (Filters[I].Type==FILTER_NONE) + EmptyCount++; + } + if (EmptyCount>0) + Filters.Alloc(Filters.Size()-EmptyCount); + + if (!NotAllFiltersProcessed) // Only if all filters are processed. + { + // Write data left after last filter. + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; + } + + // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE + // instead of potentially huge MaxWinSize blocks. It also allows us + // to keep the size of Filters array reasonable. + WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; + + // Choose the nearest among WriteBorder and WrPtr actual written border. + // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. + if (WriteBorder==UnpPtr || + WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) + WriteBorder=WrPtr; +} + + +byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) +{ + byte *SrcData=Data; + switch(Flt->Type) + { + case FILTER_E8: + case FILTER_E8E9: + { + uint FileOffset=(uint)WrittenFileSize; + + const uint FileSize=0x1000000; + byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; + // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" + // to avoid overflow for DataSize<4. + for (uint CurPos=0;CurPos+4<DataSize;) + { + byte CurByte=*(Data++); + CurPos++; + if (CurByte==0xe8 || CurByte==CmpByte2) + { + uint Offset=(CurPos+FileOffset)%FileSize; + uint Addr=RawGet4(Data); + + // We check 0x80000000 bit instead of '< 0' comparison + // not assuming int32 presence or uint size and endianness. + if ((Addr & 0x80000000)!=0) // Addr<0 + { + if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0 + RawPut4(Addr+FileSize,Data); + } + else + if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize + RawPut4(Addr-Offset,Data); + + Data+=4; + CurPos+=4; + } + } + } + return SrcData; + case FILTER_ARM: + // 2019-11-15: we turned off ARM filter by default when compressing, + // mostly because it is inefficient for modern 64 bit ARM binaries. + // It was turned on by default in 5.0 - 5.80b3 , so we still need it + // here for compatibility with some of previously created archives. + { + uint FileOffset=(uint)WrittenFileSize; + // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3" + // to avoid overflow for DataSize<3. + for (uint CurPos=0;CurPos+3<DataSize;CurPos+=4) + { + byte *D=Data+CurPos; + if (D[3]==0xeb) // BL command with '1110' (Always) condition. + { + uint Offset=D[0]+uint(D[1])*0x100+uint(D[2])*0x10000; + Offset-=(FileOffset+CurPos)/4; + D[0]=(byte)Offset; + D[1]=(byte)(Offset>>8); + D[2]=(byte)(Offset>>16); + } + } + } + return SrcData; + case FILTER_DELTA: + { + // Unlike RAR3, we do not need to reject excessive channel + // values here, since RAR5 uses only 5 bits to store channel. + uint Channels=Flt->Channels,SrcPos=0; + + FilterDstMemory.Alloc(DataSize); + byte *DstData=&FilterDstMemory[0]; + + // 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;CurChannel<Channels;CurChannel++) + { + byte PrevByte=0; + for (uint DestPos=CurChannel;DestPos<DataSize;DestPos+=Channels) + DstData[DestPos]=(PrevByte-=Data[SrcPos++]); + } + return DstData; + } + + } + return NULL; +} + + +void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr) +{ + if (EndPtr!=StartPtr) + UnpSomeRead=true; + if (EndPtr<StartPtr) + UnpAllBuf=true; + + if (Fragmented) + { + size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask; + while (SizeToWrite>0) + { + size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); + UnpWriteData(&FragWindow[StartPtr],BlockSize); + SizeToWrite-=BlockSize; + StartPtr=(StartPtr+BlockSize) & MaxWinMask; + } + } + else + if (EndPtr<StartPtr) + { + UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr); + UnpWriteData(Window,EndPtr); + } + else + UnpWriteData(Window+StartPtr,EndPtr-StartPtr); +} + + +void Unpack::UnpWriteData(byte *Data,size_t Size) +{ + if (WrittenFileSize>=DestUnpSize) + return; + size_t WriteSize=Size; + int64 LeftToWrite=DestUnpSize-WrittenFileSize; + if ((int64)WriteSize>LeftToWrite) + WriteSize=(size_t)LeftToWrite; + UnpIO->UnpWrite(Data,WriteSize); + WrittenFileSize+=Size; +} + + +void Unpack::UnpInitData50(bool Solid) +{ + if (!Solid) + TablesRead5=false; +} + + +bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) +{ + Header.HeaderSize=0; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) + if (!UnpReadBuf()) + return false; + Inp.faddbits((8-Inp.InBit)&7); + + byte BlockFlags=Inp.fgetbits()>>8; + Inp.faddbits(8); + uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + + if (ByteCount==4) + return false; + + Header.HeaderSize=2+ByteCount; + + Header.BlockBitSize=(BlockFlags&7)+1; + + byte SavedCheckSum=Inp.fgetbits()>>8; + Inp.faddbits(8); + + int BlockSize=0; + for (uint I=0;I<ByteCount;I++) + { + BlockSize+=(Inp.fgetbits()>>8)<<(I*8); + Inp.addbits(8); + } + + Header.BlockSize=BlockSize; + byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + if (CheckSum!=SavedCheckSum) + return false; + + Header.BlockStart=Inp.InAddr; + ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); + + Header.LastBlockInFile=(BlockFlags & 0x40)!=0; + Header.TablePresent=(BlockFlags & 0x80)!=0; + return true; +} + + +bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) +{ + if (!Header.TablePresent) + return true; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) + if (!UnpReadBuf()) + return false; + + byte BitLength[BC]; + for (uint I=0;I<BC;I++) + { + uint Length=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && I<ASIZE(BitLength)) + BitLength[I++]=0; + I--; + } + } + else + BitLength[I]=Length; + } + + MakeDecodeTables(BitLength,&Tables.BD,BC); + + byte Table[HUFF_TABLE_SIZE]; + const uint TableSize=HUFF_TABLE_SIZE; + for (uint I=0;I<TableSize;) + { + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-5) + if (!UnpReadBuf()) + return false; + uint Number=DecodeNumber(Inp,&Tables.BD); + if (Number<16) + { + Table[I]=Number; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + { + // We cannot have "repeat previous" code at the first position. + // Multiple such codes would shift Inp position without changing I, + // which can lead to reading beyond of Inp boundary in mutithreading + // mode, where Inp.ExternalBuffer disables bounds check and we just + // reserve a lot of buffer space to not need such check normally. + return false; + } + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } + } + else + { + uint N; + if (Number==18) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && I<TableSize) + Table[I++]=0; + } + } + TablesRead5=true; + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) + return false; + MakeDecodeTables(&Table[0],&Tables.LD,NC); + MakeDecodeTables(&Table[NC],&Tables.DD,DC); + MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); + MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + return true; +} + + +void Unpack::InitFilters() +{ + Filters.SoftReset(); +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50frag.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50frag.cpp new file mode 100644 index 00000000..3c008ff2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50frag.cpp @@ -0,0 +1,103 @@ +FragmentedWindow::FragmentedWindow() +{ + memset(Mem,0,sizeof(Mem)); + memset(MemSize,0,sizeof(MemSize)); +} + + +FragmentedWindow::~FragmentedWindow() +{ + Reset(); +} + + +void FragmentedWindow::Reset() +{ + for (uint I=0;I<ASIZE(Mem);I++) + if (Mem[I]!=NULL) + { + free(Mem[I]); + Mem[I]=NULL; + } +} + + +void FragmentedWindow::Init(size_t WinSize) +{ + Reset(); + + uint BlockNum=0; + size_t TotalSize=0; // Already allocated. + while (TotalSize<WinSize && BlockNum<ASIZE(Mem)) + { + size_t Size=WinSize-TotalSize; // Size needed to allocate. + + // Minimum still acceptable block size. Next allocations cannot be larger + // than current, so we do not need blocks if they are smaller than + // "size left / attempts left". Also we do not waste time to blocks + // smaller than some arbitrary constant. + size_t MinSize=Max(Size/(ASIZE(Mem)-BlockNum), 0x400000); + + byte *NewMem=NULL; + while (Size>=MinSize) + { + NewMem=(byte *)malloc(Size); + if (NewMem!=NULL) + break; + Size-=Size/32; + } + if (NewMem==NULL) + throw std::bad_alloc(); + + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access to unused areas of sliding dictionary. + memset(NewMem,0,Size); + + Mem[BlockNum]=NewMem; + TotalSize+=Size; + MemSize[BlockNum]=TotalSize; + BlockNum++; + } + if (TotalSize<WinSize) // Not found enough free blocks. + throw std::bad_alloc(); +} + + +byte& FragmentedWindow::operator [](size_t Item) +{ + if (Item<MemSize[0]) + return Mem[0][Item]; + for (uint I=1;I<ASIZE(MemSize);I++) + if (Item<MemSize[I]) + return Mem[I][Item-MemSize[I-1]]; + return Mem[0][0]; // Must never happen; +} + + +void FragmentedWindow::CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask) +{ + size_t SrcPtr=UnpPtr-Distance; + while (Length-- > 0) + { + (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size) +{ + for (size_t I=0;I<Size;I++) + Dest[I]=(*this)[WinPos+I]; +} + + +size_t FragmentedWindow::GetBlockSize(size_t StartPos,size_t RequiredSize) +{ + for (uint I=0;I<ASIZE(MemSize);I++) + if (StartPos<MemSize[I]) + return Min(MemSize[I]-StartPos,RequiredSize); + return 0; // Must never be here. +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50mt.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50mt.cpp new file mode 100644 index 00000000..691ac8e9 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpack50mt.cpp @@ -0,0 +1,655 @@ +#define UNP_READ_SIZE_MT 0x400000 +#define UNP_BLOCKS_PER_THREAD 2 + + +struct UnpackThreadDataList +{ + UnpackThreadData *D; + uint BlockCount; +}; + + +THREAD_PROC(UnpackDecodeThread) +{ + UnpackThreadDataList *DL=(UnpackThreadDataList *)Data; + for (uint I=0;I<DL->BlockCount;I++) + DL->D->UnpackPtr->UnpackDecode(DL->D[I]); +} + + +void Unpack::InitMT() +{ + if (ReadBufMT==NULL) + { + // Even getbits32 can read up to 3 additional bytes after current + // and our block header and table reading code can look much further. + // Let's allocate the additional space here, so we do not need to check + // bounds for every bit field access. + const size_t Overflow=1024; + + ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow]; + memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow); + } + if (UnpThreadData==NULL) + { + uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD; + UnpThreadData=new UnpackThreadData[MaxItems]; + memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems); + + for (uint I=0;I<MaxItems;I++) + { + UnpackThreadData *CurData=UnpThreadData+I; + if (CurData->Decoded==NULL) + { + // Typical number of items in RAR blocks does not exceed 0x4000. + CurData->DecodedAllocated=0x4100; + // It will be freed in the object destructor, not in this file. + CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem)); + if (CurData->Decoded==NULL) + ErrHandler.MemoryError(); + } + } + } +} + + +void Unpack::Unpack5MT(bool Solid) +{ + InitMT(); + UnpInitData(Solid); + + for (uint I=0;I<MaxUserThreads*UNP_BLOCKS_PER_THREAD;I++) + { + UnpackThreadData *CurData=UnpThreadData+I; + CurData->LargeBlock=false; + CurData->Incomplete=false; + } + + UnpThreadData[0].BlockHeader=BlockHeader; + UnpThreadData[0].BlockTables=BlockTables; + uint LastBlockNum=0; + + int DataSize=0; + int BlockStart=0; + + + // 'true' if we found a block too large for multithreaded extraction, + // so we switched to single threaded mode until the end of file. + // Large blocks could cause too high memory use in multithreaded mode. + bool LargeBlock=false; + + bool Done=false; + while (!Done) + { + // Data amount, which is guaranteed to fit block header and tables, + // so we can safely read them without additional checks. + const int TooSmallToProcess=1024; + + int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf); + if (ReadSize<0) + break; + DataSize+=ReadSize; + if (DataSize==0) + break; + + // First read chunk can be small if we are near the end of volume + // and we want it to fit block header and tables. + if (ReadSize>0 && DataSize<TooSmallToProcess) + continue; + + while (BlockStart<DataSize && !Done) + { + uint BlockNumber=0,BlockNumberMT=0; + while (BlockNumber<MaxUserThreads*UNP_BLOCKS_PER_THREAD) + { + UnpackThreadData *CurData=UnpThreadData+BlockNumber; + LastBlockNum=BlockNumber; + CurData->UnpackPtr=this; + + // 'Incomplete' thread is present. This is a thread processing block + // in the end of buffer, split between two read operations. + if (CurData->Incomplete) + CurData->DataSize=DataSize; + else + { + CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart); + CurData->Inp.InitBitInput(); + CurData->DataSize=DataSize-BlockStart; + if (CurData->DataSize==0) + break; + CurData->DamagedData=false; + CurData->HeaderRead=false; + CurData->TableRead=false; + } + + // We should not use 'last block in file' block flag here unless + // we'll check the block size, because even if block is last in file, + // it can exceed the current buffer and require more reading. + CurData->NoDataLeft=(ReadSize==0); + + CurData->Incomplete=false; + CurData->ThreadNumber=BlockNumber; + + if (!CurData->HeaderRead) + { + CurData->HeaderRead=true; + if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) || + !CurData->BlockHeader.TablePresent && !TablesRead5) + { + Done=true; + break; + } + TablesRead5=true; + } + + // To prevent too high memory use we switch to single threaded mode + // if block exceeds this size. Typically RAR blocks do not exceed + // 64 KB, so this protection should not affect most of valid archives. + const int LargeBlockSize=0x20000; + if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize) + LargeBlock=CurData->LargeBlock=true; + else + BlockNumberMT++; // Number of normal blocks processed in MT mode. + + BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize; + + BlockNumber++; + + int DataLeft=DataSize-BlockStart; + if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile) + break; + + // For second and following threads we move smaller blocks to buffer + // start to ensure that we have enough data to fit block header + // and tables. + if (DataLeft<TooSmallToProcess) + break; + } + +//#undef USE_THREADS + UnpackThreadDataList UTDArray[MaxPoolThreads]; + uint UTDArrayPos=0; + + uint MaxBlockPerThread=BlockNumberMT/MaxUserThreads; + if (BlockNumberMT%MaxUserThreads!=0) + MaxBlockPerThread++; + + // Decode all normal blocks until the first 'large' if any. + for (uint CurBlock=0;CurBlock<BlockNumberMT;CurBlock+=MaxBlockPerThread) + { + UnpackThreadDataList *UTD=UTDArray+UTDArrayPos++; + UTD->D=UnpThreadData+CurBlock; + UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock); + +#ifdef USE_THREADS + if (BlockNumber==1) + UnpackDecode(*UTD->D); + else + UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD); +#else + for (uint I=0;I<UTD->BlockCount;I++) + UnpackDecode(UTD->D[I]); +#endif + } + + if (BlockNumber==0) + break; + +#ifdef USE_THREADS + UnpThreadPool->WaitDone(); +#endif + + bool IncompleteThread=false; + + for (uint Block=0;Block<BlockNumber;Block++) + { + UnpackThreadData *CurData=UnpThreadData+Block; + if (!CurData->LargeBlock && !ProcessDecoded(*CurData) || + CurData->LargeBlock && !UnpackLargeBlock(*CurData) || + CurData->DamagedData) + { + Done=true; + break; + } + if (CurData->Incomplete) + { + int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT); + if (DataSize<=BufPos) // Thread exceeded input buffer boundary. + { + Done=true; + break; + } + IncompleteThread=true; + memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos); + CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart; + CurData->BlockHeader.HeaderSize=0; + CurData->BlockHeader.BlockStart=0; + CurData->Inp.InBuf=ReadBufMT; + CurData->Inp.InAddr=0; + + if (Block!=0) + { + // Move the incomplete thread entry to the first position, + // so we'll start processing from it. Preserve the original + // buffer for decoded data. + UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded; + uint DecodedAllocated=UnpThreadData[0].DecodedAllocated; + UnpThreadData[0]=*CurData; + UnpThreadData[0].Decoded=Decoded; + UnpThreadData[0].DecodedAllocated=DecodedAllocated; + CurData->Incomplete=false; + } + + BlockStart=0; + DataSize-=BufPos; + break; + } + else + if (CurData->BlockHeader.LastBlockInFile) + { + Done=true; + break; + } + } + + if (IncompleteThread || Done) + break; // Current buffer is done, read more data or quit. + else + { + int DataLeft=DataSize-BlockStart; + if (DataLeft<TooSmallToProcess) + { + if (DataLeft<0) // Invalid data, must not happen in valid archive. + { + Done=true; + break; + } + + // If we do not have incomplete thread and have some data + // in the end of buffer, too small for single thread, + // let's move it to beginning of next buffer. + if (DataLeft>0) + memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft); + DataSize=DataLeft; + BlockStart=0; + break; // Current buffer is done, try to read more data. + } + } + } + } + UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here. + UnpWriteBuf(); + + BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; + BlockTables=UnpThreadData[LastBlockNum].BlockTables; +} + + +// Decode Huffman block and save decoded data to memory. +void Unpack::UnpackDecode(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return; + } + + D.DecodedSize=0; + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots. + { + D.DecodedAllocated=D.DecodedAllocated*2; + void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem)); + if (Decoded==NULL) + ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor. + D.Decoded=(UnpackDecodedItem *)Decoded; + } + + UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++; + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + if (D.DecodedSize>1) + { + UnpackDecodedItem *PrevItem=CurItem-1; + if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<3) + { + PrevItem->Length++; + PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; + D.DecodedSize--; + continue; + } + } + CurItem->Type=UNPDT_LITERAL; + CurItem->Literal[0]=(byte)MainSlot; + CurItem->Length=0; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits32()>>(32-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + CurItem->Type=UNPDT_MATCH; + CurItem->Length=(ushort)Length; + CurItem->Distance=Distance; + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + ReadFilter(D.Inp,Filter); + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Type; + CurItem->Distance=Filter.BlockStart; + + CurItem=D.Decoded+D.DecodedSize++; + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Channels; + CurItem->Distance=Filter.BlockLength; + + continue; + } + if (MainSlot==257) + { + CurItem->Type=UNPDT_FULLREP; + continue; + } + if (MainSlot<262) + { + CurItem->Type=UNPDT_REP; + CurItem->Distance=MainSlot-258; + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + CurItem->Length=(ushort)Length; + continue; + } + } +} + + +// Process decoded Huffman block data. +bool Unpack::ProcessDecoded(UnpackThreadData &D) +{ + UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; + while (Item<Border) + { + UnpPtr&=MaxWinMask; + if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return false; + } + + if (Item->Type==UNPDT_LITERAL) + { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + if (Item->Length==3 && UnpPtr<MaxWinSize-4) + { + *(uint32 *)(Window+UnpPtr)=*(uint32 *)Item->Literal; + UnpPtr+=4; + } + else +#endif + for (uint I=0;I<=Item->Length;I++) + Window[UnpPtr++ & MaxWinMask]=Item->Literal[I]; + } + else + if (Item->Type==UNPDT_MATCH) + { + InsertOldDist(Item->Distance); + LastLength=Item->Length; + CopyString(Item->Length,Item->Distance); + } + else + if (Item->Type==UNPDT_REP) + { + uint Distance=OldDist[Item->Distance]; + for (uint I=Item->Distance;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + LastLength=Item->Length; + CopyString(Item->Length,Distance); + } + else + if (Item->Type==UNPDT_FULLREP) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + } + else + if (Item->Type==UNPDT_FILTER) + { + UnpackFilter Filter; + + Filter.Type=(byte)Item->Length; + Filter.BlockStart=Item->Distance; + + Item++; + + Filter.Channels=(byte)Item->Length; + Filter.BlockLength=Item->Distance; + + AddFilter(Filter); + } + Item++; + } + return true; +} + + +// For large blocks we decode and process in same function in single threaded +// mode, so we do not need to store intermediate data in memory. +bool Unpack::UnpackLargeBlock(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return false; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return false; + } + + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + UnpPtr&=MaxWinMask; + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return false; + } + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits32()>>(32-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + } + return true; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/unpackinline.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/unpackinline.cpp new file mode 100644 index 00000000..04c3d1f7 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/unpackinline.cpp @@ -0,0 +1,147 @@ +_forceinline void Unpack::InsertOldDist(uint Distance) +{ + OldDist[3]=OldDist[2]; + OldDist[2]=OldDist[1]; + OldDist[1]=OldDist[0]; + OldDist[0]=Distance; +} + +#ifdef _MSC_VER +#define FAST_MEMCPY +#endif + +_forceinline void Unpack::CopyString(uint Length,uint Distance) +{ + size_t SrcPtr=UnpPtr-Distance; + if (SrcPtr<MaxWinSize-MAX_INC_LZ_MATCH && UnpPtr<MaxWinSize-MAX_INC_LZ_MATCH) + { + // If we are not close to end of window, we do not need to waste time + // to "& MaxWinMask" pointer protection. + + byte *Src=Window+SrcPtr; + byte *Dest=Window+UnpPtr; + UnpPtr+=Length; + +#ifdef FAST_MEMCPY + if (Distance<Length) // Overlapping strings +#endif + while (Length>=8) + { + Dest[0]=Src[0]; + Dest[1]=Src[1]; + Dest[2]=Src[2]; + Dest[3]=Src[3]; + Dest[4]=Src[4]; + Dest[5]=Src[5]; + Dest[6]=Src[6]; + Dest[7]=Src[7]; + + Src+=8; + Dest+=8; + Length-=8; + } +#ifdef FAST_MEMCPY + else + while (Length>=8) + { + // In theory we still could overlap here. + // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). + // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH + // always, so overlap here is impossible. + + // This memcpy expanded inline by MSVC. We could also use uint64 + // assignment, which seems to provide about the same speed. + memcpy(Dest,Src,8); + + Src+=8; + Dest+=8; + Length-=8; + } +#endif + + // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. + if (Length>0) { Dest[0]=Src[0]; + if (Length>1) { Dest[1]=Src[1]; + if (Length>2) { Dest[2]=Src[2]; + if (Length>3) { Dest[3]=Src[3]; + if (Length>4) { Dest[4]=Src[4]; + if (Length>5) { Dest[5]=Src[5]; + if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. + } + else + while (Length-- > 0) // Slow copying with all possible precautions. + { + Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +_forceinline uint Unpack::DecodeNumber(BitInput &Inp,DecodeTable *Dec) +{ + // Left aligned 15 bit length raw bit field. + uint BitField=Inp.getbits() & 0xfffe; + + if (BitField<Dec->DecodeLen[Dec->QuickBits]) + { + uint Code=BitField>>(16-Dec->QuickBits); + Inp.addbits(Dec->QuickLen[Code]); + return Dec->QuickNum[Code]; + } + + // Detect the real bit length for current code. + uint Bits=15; + for (uint I=Dec->QuickBits+1;I<15;I++) + if (BitField<Dec->DecodeLen[I]) + { + Bits=I; + break; + } + + Inp.addbits(Bits); + + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[Bits-1]; + + // Start codes are left aligned, but we need the normal right aligned + // number. So we shift the distance to the right. + Dist>>=(16-Bits); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos=Dec->DecodePos[Bits]+Dist; + + // Out of bounds safety check required for damaged archives. + if (Pos>=Dec->MaxNum) + Pos=0; + + // Convert the position in the code list to position in alphabet + // and return it. + return Dec->DecodeNum[Pos]; +} + + +_forceinline uint Unpack::SlotToLength(BitInput &Inp,uint Slot) +{ + uint LBits,Length=2; + if (Slot<8) + { + LBits=0; + Length+=Slot; + } + else + { + LBits=Slot/4-1; + Length+=(4 | (Slot & 3)) << LBits; + } + + if (LBits>0) + { + Length+=Inp.getbits()>>(16-LBits); + Inp.addbits(LBits); + } + return Length; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/uowners.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/uowners.cpp new file mode 100644 index 00000000..9f463085 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/uowners.cpp @@ -0,0 +1,141 @@ + + +void ExtractUnixOwner20(Archive &Arc,const wchar *FileName) +{ + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + if (Arc.BrokenHeader) + { + uiMsg(UIERROR_UOWNERBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + struct passwd *pw; + errno=0; // Required by getpwnam specification if we need to check errno. + if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL) + { + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(Arc.UOHead.OwnerName)); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + uid_t OwnerID=pw->pw_uid; + + struct group *gr; + errno=0; // Required by getgrnam specification if we need to check errno. + if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL) + { + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(Arc.UOHead.GroupName)); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + uint Attr=GetFileAttr(FileName); + gid_t GroupID=gr->gr_gid; +#if defined(SAVE_LINKS) && !defined(_APPLE) + if (lchown(NameA,OwnerID,GroupID)!=0) +#else + if (chown(NameA,OwnerID,GroupID)!=0) +#endif + { + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); + } + SetFileAttr(FileName,Attr); +} + + +void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) +{ + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + char *OwnerName=(char *)&Arc.SubHead.SubData[0]; + int OwnerSize=strlen(OwnerName)+1; + int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; + char GroupName[NM]; + strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize); + GroupName[GroupSize]=0; + + struct passwd *pw; + if ((pw=getpwnam(OwnerName))==NULL) + { + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(OwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + uid_t OwnerID=pw->pw_uid; + + struct group *gr; + if ((gr=getgrnam(GroupName))==NULL) + { + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + uint Attr=GetFileAttr(FileName); + gid_t GroupID=gr->gr_gid; +#if defined(SAVE_LINKS) && !defined(_APPLE) + if (lchown(NameA,OwnerID,GroupID)!=0) +#else + if (chown(NameA,OwnerID,GroupID)!=0) +#endif + { + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); + } + SetFileAttr(FileName,Attr); +} + + +void SetUnixOwner(Archive &Arc,const wchar *FileName) +{ + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + // First, we try to resolve symbolic names. If they are missing or cannot + // be resolved, we try to use numeric values if any. If numeric values + // are missing too, function fails. + FileHeader &hd=Arc.FileHead; + if (*hd.UnixOwnerName!=0) + { + struct passwd *pw; + if ((pw=getpwnam(hd.UnixOwnerName))==NULL) + { + if (!hd.UnixOwnerNumeric) + { + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(hd.UnixOwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixOwnerID=pw->pw_uid; + } + if (*hd.UnixGroupName!=0) + { + struct group *gr; + if ((gr=getgrnam(hd.UnixGroupName))==NULL) + { + if (!hd.UnixGroupNumeric) + { + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(hd.UnixGroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixGroupID=gr->gr_gid; + } +#if defined(SAVE_LINKS) && !defined(_APPLE) + if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) +#else + if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) +#endif + { + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); + } +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/version.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/version.hpp new file mode 100644 index 00000000..72df1f84 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/version.hpp @@ -0,0 +1,6 @@ +#define RARVER_MAJOR 6 +#define RARVER_MINOR 11 +#define RARVER_BETA 0 +#define RARVER_DAY 3 +#define RARVER_MONTH 3 +#define RARVER_YEAR 2022 diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/volume.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/volume.cpp new file mode 100644 index 00000000..917851de --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/volume.cpp @@ -0,0 +1,268 @@ +#include "rar.hpp" + +#ifdef RARDLL +static bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize); +static bool DllVolNotify(RAROptions *Cmd,wchar *NextName); +#endif + + + +bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) +{ + RAROptions *Cmd=Arc.GetRAROptions(); + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; + bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) && + hd->SplitAfter; + + if (DataIO!=NULL && SplitHeader) + { + bool PackedHashPresent=Arc.Format==RARFMT50 || + hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff; + if (PackedHashPresent && + !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL)) + uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName); + } + + bool PrevVolEncrypted=Arc.Encrypted; + + int64 PosBeforeClose=Arc.Tell(); + + if (DataIO!=NULL) + DataIO->ProcessedArcSize+=DataIO->LastArcSize; + + + Arc.Close(); + + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + bool RecoveryDone=false; +#endif + bool OldSchemeTested=false; + + bool FailedOpen=false; // No more next volume open attempts if true. +#if !defined(SILENT) + // In -vp mode we force the pause before next volume even if it is present + // and even if we are on the hard disk. It is important when user does not + // want to process partially downloaded volumes preliminary. + // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause + // if we could open the next volume with FMF_OPENEXCLUSIVE. But another + // developer asked us to return the previous behavior and always prompt + // for confirmation. They want to control when unrar continues, because + // the next file might not be fully decoded yet. They write chunks of data + // and then close the file again until the next chunk comes in. + + if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) + FailedOpen=true; +#endif + + uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; + + if (!FailedOpen) + while (!Arc.Open(NextName,OpenMode)) + { + // We need to open a new volume which size was not calculated + // in total size before, so we cannot calculate the total progress + // anymore. Let's reset the total size to zero and stop + // the total progress. + if (DataIO!=NULL) + DataIO->TotalArcSize=0; + + if (!OldSchemeTested) + { + // Checking for new style volumes renamed by user to old style + // name format. Some users did it for unknown reason. + wchar AltNextName[NM]; + wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName)); + NextVolumeName(AltNextName,ASIZE(AltNextName),true); + OldSchemeTested=true; + if (Arc.Open(AltNextName,OpenMode)) + { + wcsncpyz(NextName,AltNextName,ASIZE(NextName)); + break; + } + } +#ifdef RARDLL + if (!DllVolChange(Cmd,NextName,ASIZE(NextName))) + { + FailedOpen=true; + break; + } +#else // !RARDLL + +#ifndef SFX_MODULE + if (!RecoveryDone) + { + RecVolumesRestore(Cmd,Arc.FileName,true); + RecoveryDone=true; + continue; + } +#endif + + if (!Cmd->VolumePause && !IsRemovable(NextName)) + { + FailedOpen=true; + break; + } +#ifndef SILENT + if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName))) +#endif + { + FailedOpen=true; + break; + } + +#endif // RARDLL + } + + if (FailedOpen) + { + uiMsg(UIERROR_MISSINGVOL,NextName); + Arc.Open(Arc.FileName,OpenMode); + Arc.Seek(PosBeforeClose,SEEK_SET); + return false; + } + + if (Command=='T' || Command=='X' || Command=='E') + mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); + + + Arc.CheckArc(true); +#ifdef RARDLL + if (!DllVolNotify(Cmd,NextName)) + return false; +#endif + + if (Arc.Encrypted!=PrevVolEncrypted) + { + // There is no legitimate reason for encrypted header state to be + // changed in the middle of volume sequence. So we abort here to prevent + // replacing an encrypted header volume to unencrypted and adding + // unexpected files by third party to encrypted extraction. + uiMsg(UIERROR_BADARCHIVE,Arc.FileName); + ErrHandler.Exit(RARX_FATAL); + } + + if (SplitHeader) + Arc.SearchBlock(HeaderType); + else + Arc.ReadHeader(); + if (Arc.GetHeaderType()==HEAD_FILE) + { + Arc.ConvertAttributes(); + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); + } + if (ShowFileName && !Cmd->DisableNames) + { + mprintf(St(MExtrPoints),Arc.FileHead.FileName); + if (!Cmd->DisablePercentage) + mprintf(L" "); + } + if (DataIO!=NULL) + { + if (HeaderType==HEAD_ENDARC) + DataIO->UnpVolume=false; + else + { + DataIO->UnpVolume=hd->SplitAfter; + DataIO->SetPackedSizeToRead(hd->PackSize); + } + + DataIO->AdjustTotalArcSize(&Arc); + + // Reset the size of packed data read from current volume. It is used + // to display the total progress and preceding volumes are already + // compensated with ProcessedArcSize, so we need to reset this variable. + DataIO->CurUnpRead=0; + + DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads); + } + return true; +} + + + + + + +#ifdef RARDLL +bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize) +{ + bool DllVolChanged=false,DllVolAborted=false; + + if (Cmd->Callback!=NULL) + { + wchar OrgNextName[NM]; + wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName)); + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (wcscmp(OrgNextName,NextName)!=0) + DllVolChanged=true; + else + { + char NextNameA[NM],OrgNextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA)); + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (strcmp(OrgNextNameA,NextNameA)!=0) + { + // We can damage some Unicode characters by U->A->U conversion, + // so set Unicode name only if we see that ANSI name is changed. + CharToWide(NextNameA,NextName,NameSize); + DllVolChanged=true; + } + } + } + if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) + { + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); + if (RetCode==0) + DllVolAborted=true; + else + CharToWide(NextNameA,NextName,NameSize); + } + + // We quit only on 'abort' condition, but not on 'name not changed'. + // It is legitimate for program to return the same name when waiting + // for currently non-existent volume. + // Also we quit to prevent an infinite loop if no callback is defined. + if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL) + { + Cmd->DllError=ERAR_EOPEN; + return false; + } + return true; +} +#endif + + +#ifdef RARDLL +bool DllVolNotify(RAROptions *Cmd,wchar *NextName) +{ + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + if (Cmd->Callback!=NULL) + { + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) + return false; + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1) + return false; + } + if (Cmd->ChangeVolProc!=NULL) + { + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); + if (RetCode==0) + return false; + } + return true; +} +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/volume.hpp b/Src/external_dependencies/openmpt-trunk/include/unrar/volume.hpp new file mode 100644 index 00000000..2d6a6d5c --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/volume.hpp @@ -0,0 +1,10 @@ +#ifndef _RAR_VOLUME_ +#define _RAR_VOLUME_ + +void SplitArchive(Archive &Arc,FileHeader *fh,int64 *HeaderPos, + ComprDataIO *DataIO); +bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, + wchar Command); +void SetVolWrite(Archive &Dest,int64 VolSize); + +#endif diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/win32acl.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/win32acl.cpp new file mode 100644 index 00000000..8dec4fd6 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/win32acl.cpp @@ -0,0 +1,137 @@ +static void SetACLPrivileges(); + +static bool ReadSacl=false; + + + +#ifndef SFX_MODULE +void ExtractACL20(Archive &Arc,const wchar *FileName) +{ + SetACLPrivileges(); + + if (Arc.BrokenHeader) + { + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>VER_PACK) + { + uiMsg(UIERROR_ACLUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + + ComprDataIO DataIO; + Unpack Unpack(&DataIO); + Unpack.Init(0x10000,false); + + Array<byte> UnpData(Arc.EAHead.UnpSize); + DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); + DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); + DataIO.EnableShowProgress(false); + DataIO.SetFiles(&Arc,NULL); + DataIO.UnpHash.Init(HASH_CRC32,1); + Unpack.SetDestSize(Arc.EAHead.UnpSize); + Unpack.DoUnpack(Arc.EAHead.UnpVer,false); + + if (Arc.EAHead.EACRC!=DataIO.UnpHash.GetCRC32()) + { + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| + DACL_SECURITY_INFORMATION; + if (ReadSacl) + si|=SACL_SECURITY_INFORMATION; + SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0]; + + int SetCode=SetFileSecurity(FileName,si,sd); + + if (!SetCode) + { + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); + ErrHandler.SysErrMsg(); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); + } +} +#endif + + +void ExtractACL(Archive &Arc,const wchar *FileName) +{ + Array<byte> SubData; + if (!Arc.ReadSubData(&SubData,NULL,false)) + return; + + return; // OPENMPT ADDITION + SetACLPrivileges(); + + SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| + DACL_SECURITY_INFORMATION; + if (ReadSacl) + si|=SACL_SECURITY_INFORMATION; + SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0]; + + int SetCode=SetFileSecurity(FileName,si,sd); + if (!SetCode) + { + wchar LongName[NM]; + if (GetWinLongPath(FileName,LongName,ASIZE(LongName))) + SetCode=SetFileSecurity(LongName,si,sd); + } + + if (!SetCode) + { + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); + ErrHandler.SysErrMsg(); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); + } +} + + +void SetACLPrivileges() +{ + return; // OPENMPT ADDITION + static bool InitDone=false; + if (InitDone) + return; + + if (SetPrivilege(SE_SECURITY_NAME)) + ReadSacl=true; + SetPrivilege(SE_RESTORE_NAME); + + InitDone=true; +} + + +bool SetPrivilege(LPCTSTR PrivName) +{ + bool Success=false; + + HANDLE hToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) && + AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && + GetLastError() == ERROR_SUCCESS) + Success=true; + + CloseHandle(hToken); + } + + return Success; +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/win32lnk.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/win32lnk.cpp new file mode 100644 index 00000000..ff324e5a --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/win32lnk.cpp @@ -0,0 +1,196 @@ +#define SYMLINK_FLAG_RELATIVE 1 + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + + + + +bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + return false; /* // OPENMPT ADDITION + static bool PrivSet=false; + if (!PrivSet) + { + SetPrivilege(SE_RESTORE_NAME); + // Not sure if we really need it, but let's request anyway. + SetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME); + PrivSet=true; + } + + const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM+1024; + Array<byte> Buf(BufSize); + REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; + + wchar SubstName[NM]; + wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName)); + size_t SubstLength=wcslen(SubstName); + + wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName; + bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0; + if (WinPrefix) + PrintNameSrc+=4; + if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0) + { + *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name. + PrintNameSrc+=3; + } + wcscpy(PrintNameDst,PrintNameSrc); + + size_t PrintLength=wcslen(PrintName); + + bool AbsPath=WinPrefix; + // IsFullPath is not really needed here, AbsPath check is enough. + // We added it just for extra safety, in case some Windows version would + // allow to create absolute targets with SYMLINK_FLAG_RELATIVE. + // Use hd->FileName instead of Name, since Name can include the destination + // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + return false; + + CreatePath(Name,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. + if (FileExist(Name)) + if (IsDir(GetFileAttr(Name))) + DelDir(Name); + else + DelFile(Name); + + // 'DirTarget' check is important for Unix symlinks to directories. + // Unix symlinks do not have their own 'directory' attribute. + if (hd->Dir || hd->DirTarget) + { + if (!CreateDirectory(Name,NULL)) + { + uiMsg(UIERROR_DIRCREATE,UINULL,Name); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + } + else + { + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); + return false; + } + CloseHandle(hFile); + } + + + if (hd->RedirType==FSREDIR_JUNCTION) + { + rdb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->MountPointReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameLength)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->MountPointReparseBuffer.SubstituteNameOffset=0; + rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName); + + rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName); + } + else + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK) + { + rdb->ReparseTag=IO_REPARSE_TAG_SYMLINK; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.Flags)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0; + rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName); + + rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName); + + rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE; + } + else + return false; + + HANDLE hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,0,NULL, + OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT| + FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + + DWORD Returned; + if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb, + FIELD_OFFSET(REPARSE_DATA_BUFFER,GenericReparseBuffer)+ + rdb->ReparseDataLength,NULL,0,&Returned,NULL)) + { + CloseHandle(hFile); + uiMsg(UIERROR_SLINKCREATE,UINULL,Name); + + DWORD LastError=GetLastError(); + if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) && + !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + + if (hd->Dir) + RemoveDirectory(Name); + else + DeleteFile(Name); + return false; + } + File LinkFile; + LinkFile.SetHandle(hFile); + LinkFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&hd->mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&hd->ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&hd->atime); + LinkFile.Close(); + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(Name,hd->FileAttr); + return true; + */ // OPENMPT ADDITION +} diff --git a/Src/external_dependencies/openmpt-trunk/include/unrar/win32stm.cpp b/Src/external_dependencies/openmpt-trunk/include/unrar/win32stm.cpp new file mode 100644 index 00000000..eaa43be2 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/include/unrar/win32stm.cpp @@ -0,0 +1,152 @@ + + +#if !defined(SFX_MODULE) && defined(_WIN_ALL) +void ExtractStreams20(Archive &Arc,const wchar *FileName) +{ + if (Arc.BrokenHeader) + { + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) + { + uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + + wchar StreamName[NM+2]; + if (FileName[0]!=0 && FileName[1]==0) + { + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(StreamName,L".\\",ASIZE(StreamName)); + wcsncatz(StreamName,FileName,ASIZE(StreamName)); + } + else + wcsncpyz(StreamName,FileName,ASIZE(StreamName)); + if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || + Arc.StreamHead.StreamName[0]!=':') + { + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + wchar StoredName[NM]; + CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); + ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); + + wcsncatz(StreamName,StoredName,ASIZE(StreamName)); + + FindData fd; + bool Found=FindFile::FastFind(FileName,&fd); + + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + + File CurFile; + if (CurFile.WCreate(StreamName)) + { + ComprDataIO DataIO; + Unpack Unpack(&DataIO); + Unpack.Init(0x10000,false); + + DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); + DataIO.EnableShowProgress(false); + DataIO.SetFiles(&Arc,&CurFile); + DataIO.UnpHash.Init(HASH_CRC32,1); + Unpack.SetDestSize(Arc.StreamHead.UnpSize); + Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); + + if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) + { + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); + ErrHandler.SetErrorCode(RARX_CRC); + } + else + CurFile.Close(); + } + File HostFile; + if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) + SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, + &fd.ftLastWriteTime); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr); +} +#endif + + +#ifdef _WIN_ALL +void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) +{ + wchar FullName[NM+2]; + if (FileName[0]!=0 && FileName[1]==0) + { + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(FullName,L".\\",ASIZE(FullName)); + wcsncatz(FullName,FileName,ASIZE(FullName)); + } + else + wcsncpyz(FullName,FileName,ASIZE(FullName)); + + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + if (*StreamName!=':') + { + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return; + } + + if (TestMode) + { + File CurFile; + Arc.ReadSubData(NULL,&CurFile,true); + return; + } + + wcsncatz(FullName,StreamName,ASIZE(FullName)); + + FindData fd; + bool Found=FindFile::FastFind(FileName,&fd); + + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + File CurFile; + if (CurFile.WCreate(FullName) && Arc.ReadSubData(NULL,&CurFile,false)) + CurFile.Close(); + File HostFile; + if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) + SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, + &fd.ftLastWriteTime); + + // Restoring original file attributes. Important if file was read only + // or did not have "Archive" attribute + SetFileAttr(FileName,fd.FileAttr); +} +#endif + + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize) +{ + byte *Data=&Arc.SubHead.SubData[0]; + size_t DataSize=Arc.SubHead.SubData.Size(); + if (Arc.Format==RARFMT15) + { + size_t DestSize=Min(DataSize/2,MaxSize-1); + RawToWide(Data,StreamName,DestSize); + StreamName[DestSize]=0; + } + else + { + char UtfString[NM*4]; + size_t DestSize=Min(DataSize,ASIZE(UtfString)-1); + memcpy(UtfString,Data,DestSize); + UtfString[DestSize]=0; + UtfToWide(UtfString,StreamName,MaxSize); + } +} |